Compare commits

..
116 Commits
Author SHA1 Message Date
devlikepro 5784db8055 [core] 2025.11.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-11-28 14:02:15 +07:00
devlikepro 6862063654 [core] Up GOWS 2025-11-28 14:02:14 +07:00
devlikepro 4aea282356 [core] ChatWoot - do not log binary 2025-11-28 14:02:13 +07:00
devlikepro 82ff15ae56 [core] GOWS - 10 mins deadline for media fetch 2025-11-28 14:02:12 +07:00
devlikepro 6cbf8cded2 [core] Up Dashboard - app id is uuid now 2025-11-28 14:02:11 +07:00
devlikepro 35e9418988 [core] add cpu profiling for troubleshooting 2025-11-28 14:02:10 +07:00
devlikepro a58be84e56 [core] GOWS - Use one grpc client to handle all sessions (but per session stream) 2025-11-28 14:02:09 +07:00
devlikepro 2994e0a54d [core] ignore tmp 2025-11-28 14:02:09 +07:00
devlikepro f16100e1d9 [core] GOWS - logs for grpc client closing 2025-11-28 14:02:07 +07:00
devlikepro e57be58d48 [core] GOWS - up grpc 2025-11-28 14:02:06 +07:00
devlikepro 7909910dc6 [core] ChatWoot - limit names to 255
fix #1556
2025-11-28 14:02:05 +07:00
devlikepro 7f1778ceb0 [core] ChatWoot - do not create new conversation when closed on message.ack
close devlikeapro/waha-plus#28
fix #1646
2025-11-28 14:02:05 +07:00
devlikepro aba90574d8 [core] ChatWoot - remove unused imports 2025-11-28 14:02:05 +07:00
devlikepro 33c945bf95 [core] NOWEB - Up engine 2025-11-28 14:02:04 +07:00
devlikepro d40e82eb02 [core] WEBJS - up engine 2025-11-28 14:02:04 +07:00
devlikepro 6e82d6f40b [core] GOWS - force to use cgo dns
fix #1649
2025-11-28 14:02:03 +07:00
devlikepro e193eb5696 [core] Up go 1.24
Tho we don't built anything here
2025-11-28 14:02:03 +07:00
devlikepro 8c63c4b097 [core] WEBJS - fix code pairing
fix #1651
2025-11-28 14:02:03 +07:00
devlikepro 4127d609cf [core] for-swagger 2025-11-28 14:02:03 +07:00
devlikepro 57f6db4252 [core] 2025.11.2
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-11-14 10:23:33 +07:00
devlikepro 68a91cb55c [core] up GOWS v1.0.21 2025-11-14 10:23:32 +07:00
devlikepro 869a343e43 [core] API - mentions for send image, file, video 2025-11-14 10:23:32 +07:00
devlikepro fd2b22b339 [core] Up better-sqlite3 to 12.4.1 2025-11-14 10:23:31 +07:00
devlikepro 90d3a5b1ad [core] NOWEB - add pool for sqlite3 2025-11-14 10:23:31 +07:00
devlikepro b4a2ad6b96 [core] WAHA_SQLITE_ENGINE - Add a way to use better-sqlite3 (sync sqlite3 engine) 2025-11-14 10:23:31 +07:00
devlikepro 27ff7e7a21 [core] Reset presence on engine restart (end) 2025-11-14 10:23:30 +07:00
devlikepro 94ae624662 [core] Set to OFFLINE always 2025-11-14 10:23:30 +07:00
devlikepro 52e018aa93 [core] Use timestamps.activity 2025-11-14 10:23:29 +07:00
Berg Pinheiro 8bd13a233e [core] Add automatic presence management and activity tracking
fix #1584
closes #1586

This prevents WhatsApp from disconnecting sessions due to inactivity by
signaling activity through presence updates while avoiding constant online
status that could generate unnecessary notifications.
2025-11-14 10:23:28 +07:00
Emir Muhammadzadeh 87f79c1dc5 [core] NOWEB, GOWS - add location and vcards
closes #1609
2025-11-14 10:23:28 +07:00
devlikepro 31533b5d19 [core] GOWS - move waproto helper to engine 2025-11-14 10:23:27 +07:00
devlikepro 27cc9f4764 [core] Add opustags 2025-11-14 10:23:27 +07:00
devlikepro 4fdfce3944 [core] 2025.11.1
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-11-07 13:55:03 +07:00
devlikepro 1e12a83cdf [core] Up GOWS
Fixes for poll.vote using @lid
fix #1561
fix #1570

Ignore status@broadcast for ack updates
fix #1557

Increase gRPC limit to 512MB
fix #1365
2025-11-07 13:55:03 +07:00
devlikepro 1c41b8be65 [core] Add mentions: ["all"] to /api/sendText in groups - WEBJS, GOWS, NOWEB
fix #1377
2025-11-07 13:55:03 +07:00
devlikepro f72ee7c1f8 [core] Add /api/:session/groups/:id/participants/v2 - WEBJS, GOWS, NOWEB 2025-11-07 13:55:03 +07:00
devlikepro e75912435b [core] GOWS - support mentions
fix #1372
2025-11-07 13:55:02 +07:00
devlikepro da294a58a6 [core] GOWS - increase gRPC limits to 512MB
fix #1365
2025-11-07 13:55:01 +07:00
devlikepro 7689199e11 [core] ChatWoot - no chatwoot/waha mention in links for jobs 2025-11-07 13:55:01 +07:00
devlikepro 324ab11f09 [core] Fix Chat UI on dashboard (sortBy: messageTimestamp in /messages)
fix #1602
2025-11-07 13:55:00 +07:00
devlikepro b1f2e17e3d [core] Up NOWEB
fix #1590
fix #1605
2025-11-07 13:55:00 +07:00
devlikepro 90822cd582 [core] Up WEBJS
fix #1583
fix #1546
2025-11-07 13:54:59 +07:00
devlikepro 9eec72b641 [core] dotenv 2025-11-07 13:54:59 +07:00
devlikepro 050c652480 [core] Exclude /ping and /health using WAHA_API_KEY_EXCLUDE_PATH from all auth
fix #1562
2025-11-07 13:54:59 +07:00
devlikepro c44680c987 [core] 2025.10.5
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 / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (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-11-04 14:00:34 +07:00
devlikepro 9394509a07 [core] ChatWoot - timestamp for old messages 2025-11-04 14:00:33 +07:00
devlikepro 63890dc499 [core] ChatWoot - hour12 2025-11-04 14:00:33 +07:00
devlikepro e2330901e4 [core] ChatWoot - resolve conversations option 2025-11-04 14:00:32 +07:00
devlikepro 58672fa1e1 [core] ChatWoot - sent from at the top 2025-11-04 14:00:32 +07:00
devlikepro 3890071f77 [core] ChatWoot - Album Message
fix #1405

closes devlikeapro/waha-plus#24

(cherry picked from bergpinheiro/waha-plus@947fc000bb)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-11-04 14:00:31 +07:00
devlikepro d60dfba90a [core] ChatWoot - no from me tag for history (from whatsapp or API) 2025-11-04 14:00:31 +07:00
devlikepro 4e58713e7e [core] Up GOWS - sort messages API, ctx 2025-11-04 14:00:31 +07:00
devlikepro 6f0af3c7cc [core] Up dashboard - chatwoot queue commands 2025-11-04 14:00:30 +07:00
devlikepro 23bac2e7b1 [core] ChatWoot - no additional content if we know it sent from CW 2025-11-04 14:00:30 +07:00
devlikepro e839203dbd [core] i18n 2025-11-04 14:00:30 +07:00
devlikepro 304b0037ec [core] ChatWoot - queue management 2025-11-04 14:00:28 +07:00
devlikepro 482dbbeb0e [core] ChatWoot - messages pull
[core] ChatWoot - HasMediaNoMedia message

fix #1231
2025-11-04 14:00:26 +07:00
devlikepro 8f4c79baf4 [core] Up ChatWoot to 4.7.0 2025-11-04 14:00:26 +07:00
devlikepro 31038a9318 [core] redis lock - trace log 2025-11-04 14:00:26 +07:00
devlikepro 86fde1f670 [core] WEBJS - fix get messages offset 2025-11-04 14:00:26 +07:00
devlikepro 984ef0c6a1 [core] NOWEB - Ignore history sync notification 2025-11-04 14:00:25 +07:00
devlikepro 744ee1abb1 [core] ChatWoot - Increase CONVERSATION_MESSAGE_PER_MINUTE_LIMIT to 3000 by default for "messages pull" 2025-11-04 14:00:25 +07:00
devlikepro 47f4f2670d [core] NOWEB - sqlite3 increase timeouts 2025-11-04 14:00:24 +07:00
devlikepro f16eaed935 [core] ChatWoot - keep mapping for 365 days 2025-11-04 14:00:24 +07:00
devlikepro dfe9454a72 [core] ChatWoot - debug logging for API requests 2025-11-04 14:00:24 +07:00
devlikepro 4aef38ff04 [core] Add sortOrder, sortBy to /messages 2025-11-04 14:00:22 +07:00
devlikepro ab8de51022 [core] ChatWoot - no chatwoot in user notifications 2025-11-04 14:00:22 +07:00
devlikepro c001d7fed6 [core] ChatWoot - contacts pull
fix #1231
2025-11-04 14:00:21 +07:00
devlikepro cc056dca9c [core] ChatWoot - timeZoneName short 2025-11-04 14:00:19 +07:00
devlikepro 47f60a1626 [core] cli 2025-11-04 14:00:18 +07:00
devlikepro 98721051da [core] parse jid - normalize it before
fix #1545
2025-11-04 14:00:18 +07:00
devlikepro 5d7a6f4ff7 [core] WEBJS - normalize "from" in message.reaction
fix #1545
2025-11-04 14:00:18 +07:00
devlikepro dcb7bf033f [core] ChatWoot WEBJS - fix message.reaction
fix #1545
2025-11-04 14:00:17 +07:00
devlikepro 7d29be2d69 [core] ChatWoot - mark read only if chatwoot message found 2025-11-04 14:00:17 +07:00
devlikepro 41b669a35c [core] 2025.10.4
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-10-19 17:08:06 +07:00
devlikepro 7b5ca3bc57 [core] Up Dashboard 2025-10-19 17:08:06 +07:00
devlikepro bb0c7fd22b [core] NOWEB, GOWS - better handle message.edited
fix #1394
2025-10-19 17:08:05 +07:00
devlikepro c79958541e [core] GOWS - use proto type 2025-10-19 17:08:05 +07:00
devlikepro 94ee8863d6 [core] Up Dashboard - headers, agent name 2025-10-19 17:08:04 +07:00
devlikepro 011477262d [core] Up NOWEB
fix #1538
2025-10-19 17:08:04 +07:00
devlikeproandbergpinheiro 2e8e4cefb0 [core] Fixes inverted logic of messages read in ChatWoot
closes devlikeapro/waha-plus#20

Adapted from: bergpinheiro/read-chatwoot-invertida@ef2ff1b9de

(cherry picked from commit ef2ff1b9deceeea3c26747531e26d66e96f931fd)
(cherry picked from commit 3dc3440d2d5077279934577007f89cd640f60d9e)

Co-authored-by: bergpinheiro <bergpinheiro@users.noreply.github.com>"
2025-10-19 17:08:04 +07:00
devlikepro 5e2802c098 [core] metadata is Record 2025-10-19 17:08:04 +07:00
devlikepro 9a6250a055 [core] Add jid to MeInfo type 2025-10-19 17:08:03 +07:00
devlikepro f8cb56525a [core] has recent 2025-10-19 17:08:03 +07:00
devlikepro b5b491cb7b [core] en in waha 2025-10-19 17:08:02 +07:00
devlikepro 7eb6be70f9 [core] dev image 2025-10-19 17:08:02 +07:00
devlikepro 0de8a5fabc [core] NOWEB - Fix sending media to channels
fix #1523
2025-10-19 17:08:02 +07:00
Berg Pinheiro 9fbc18fb70 [core] Fix URL breaking in ChatWoot integration
- Fix WhatsappToMarkdown function to preserve URLs with underscores
- URLs are now protected before markdown transformations
- Prevents Sarah_Jessica_Parker from becoming Sarah*Jessica*Parker
- Add comprehensive tests for URL preservation scenarios
- Maintains existing markdown formatting for non-URL text
- All comments in English for code consistency

Fixes: Links with underscores being broken in ChatWoot messages

fix devlikeapro/waha#1522
closes devlikeapro/waha-plus#19
2025-10-19 17:08:02 +07:00
devlikepro e7ce3368b0 [core] Fix tests 2025-10-19 17:08:02 +07:00
devlikepro 729600e9c6 [core] voice 2025-10-17 11:44:55 +07:00
devlikepro 54886f385b [core] Disable pt 2025-10-17 10:41:35 +07:00
devlikepro 81854ad7ec [core] Fix smoke test
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-10-16 17:07:47 +07:00
devlikepro 5261d4f281 [core] 2025.10.3 2025-10-16 16:57:01 +07:00
devlikepro 72d9d94342 [core] NOWEB - use upstream libsignal 2025-10-16 16:57:01 +07:00
devlikepro b782ad27a2 [core] ChatWoot - mark read fix one more time 2025-10-16 16:56:36 +07:00
devlikepro d7dad5e6c2 [core] rm unused imports 2025-10-16 16:56:35 +07:00
devlikepro 8765bb4412 [core] init-waha 2025-10-16 16:56:35 +07:00
devlikepro 984be25d51 [core] Replace 111 as well 2025-10-16 16:56:30 +07:00
devlikepro 70620207dd [core] Adjust .env 2025-10-16 16:56:29 +07:00
devlikepro a96200f981 [core] Adjust message 2025-10-16 16:56:29 +07:00
devlikepro 4978abf3d8 [core] Up GOWS
fix #1503
fix #1511
2025-10-16 16:56:27 +07:00
devlikepro a67d84f21e [core] ChatWoot - List Message 2025-10-16 16:56:27 +07:00
devlikepro 91f59b38fd [core] Dashboard - Chats
fix #1454
2025-10-16 16:56:27 +07:00
devlikepro d0ec351910 [core] Fix /api/sendImage when using proxy
fix #1449
fix #1510
2025-10-16 16:56:26 +07:00
devlikepro a7b12874fb [core] Require auth by default 2025-10-16 16:56:24 +07:00
devlikepro cd2c7bdd86 [core] Up creds in .env 2025-10-16 16:56:24 +07:00
devlikepro 045077143f [core] no warning about plain key 2025-10-16 16:56:24 +07:00
devlikepro bcd2199687 [core] ChatWoot - remove (WAHA) from contact name 2025-10-16 16:56:20 +07:00
devlikepro af108eede4 [core] NOWEB - normalize @lid
fix #1493
2025-10-16 16:56:19 +07:00
devlikepro ee1cf838b8 [core] NOWEB - use IPv4 by default 2025-10-16 16:56:19 +07:00
devlikepro 0fc74895da [core] Up NOWEB - add more logs for media 2025-10-16 16:56:19 +07:00
devlikepro 394937c154 [core] 2025.10.2
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-10-14 15:21:40 +07:00
devlikepro 71f9adeff4 [core] ChatWoot - ignore fromMe in ack
mention #1492
2025-10-14 15:21:40 +07:00
devlikepro e13ccdab9c [core] ChatWoot - disable error/succeeded messages
mention #1492
mention #1395
2025-10-14 15:21:40 +07:00
devlikepro 2074748a7a [core] ChatWoot - no message.ack error
mention #1492
2025-10-14 15:21:39 +07:00
devlikepro 283d63394e [core] Up NOWEB
fix #1495
fix #1499
2025-10-14 15:21:39 +07:00
188 changed files with 12838 additions and 1840 deletions

No files matched your search

+3
View File
@@ -1,3 +1,4 @@
tmp/*
Dockerfile
docker-compose
src/core/engines/gows/proto
@@ -10,6 +11,8 @@ docs
examples
.github
.git
.envs/*
.env
# compiled output
/dist
+22 -13
View File
@@ -3,9 +3,25 @@
# https://waha.devlike.pro/docs/how-to/config/
#
# ====================
# ===== SECURITY =====
# ====================
WAHA_API_KEY=00000000000000000000000000000000
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
# Disable Dashboard or Swagger
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# ==================
# ===== COMMON =====
# ==================
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=WEBJS
# Base URL for the API (used for webhooks, file URLs, etc.)
WAHA_BASE_URL=http://localhost:3000
# WAHA_PUBLIC_URL=http://localhost:3000
@@ -15,20 +31,13 @@ WAHA_BASE_URL=http://localhost:3000
# WHATSAPP_API_PORT=3000
# WHATSAPP_API_HOSTNAME=localhost
# ====================
# ===== SECURITY =====
# ====================
# "sha512:{SHA}" format - below is "admin" api key
WAHA_API_KEY=sha512:c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec
WAHA_DASHBOARD_ENABLED=True
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=admin
WHATSAPP_SWAGGER_ENABLED=True
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=admin
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=WEBJS
# ===================
# ====== GOWS =======
# ===================
# By default, GOWS uses cgo (libc) DNS resolver
# You can ask to use golang one, but it's less reliable
# GODEBUG=netdns=go
# ===================
# ===== LOGGING =====
+1
View File
@@ -129,6 +129,7 @@ jobs:
docker run -d \
--name smoke \
--rm -p3000:3000 \
-e WAHA_API_KEY=test \
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
sleep 3
+126
View File
@@ -0,0 +1,126 @@
name: Dev
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
docker-build:
if: github.repository == 'devlikeapro/waha-plus'
runs-on: ${{ matrix.runner }}
name:
${{ matrix.engine }} - ${{ matrix.browser }} - ${{ matrix.platform }} -
${{ matrix.tag }}
strategy:
matrix:
include:
- runner: 'buildjet-4vcpu-ubuntu-2204'
tag: 'dev'
platform: 'amd64'
browser: 'chromium'
engine: 'WEBJS'
goss: 'goss-linux-amd64'
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
tag: 'dev-arm'
platform: 'linux/arm64'
browser: 'chromium'
engine: 'WEBJS'
goss: 'goss-linux-arm'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check recent changes
id: recent
run: |
count=$(git rev-list --after="24 hours ago" --count HEAD || true)
if [ "$count" -gt 0 ]; then
echo "has_recent=true" >> "$GITHUB_OUTPUT"
else
echo "has_recent=false" >> "$GITHUB_OUTPUT"
fi
- name: No recent changes summary
if: ${{ steps.recent.outputs.has_recent != 'true' }}
run:
echo "No commits in the last 24 hours; skipping build." >>
"$GITHUB_STEP_SUMMARY"
- name: Set up Docker Buildx
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/setup-buildx-action@v2
- name: Docker meta
if: ${{ steps.recent.outputs.has_recent == 'true' }}
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ vars.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=${{ matrix.tag }}
- name: Login to image repository
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
USE_BROWSER=${{ matrix.browser }}
WHATSAPP_DEFAULT_ENGINE=${{ matrix.engine }}
push: false
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Install goss
if: ${{ steps.recent.outputs.has_recent == 'true' }}
env:
GOSS_DOWNLOAD: 'https://github.com/goss-org/goss/releases/download'
GOSS_VERSION: 'v0.4.8'
run: |
curl -L https://github.com/goss-org/goss/releases/download/${{ env.GOSS_VERSION }}/${{ matrix.goss }} -o /usr/local/bin/goss
chmod +rx /usr/local/bin/goss
- name: Run smoke tests
if: ${{ steps.recent.outputs.has_recent == 'true' }}
working-directory: tests/smoke
timeout-minutes: 2
run: |
docker run -d \
--name smoke \
--rm -p3000:3000 \
-e WAHA_API_KEY=test \
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
sleep 3
docker logs smoke
goss validate --retry-timeout 30s --sleep 1s
docker rm --force smoke
- name: Push
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
USE_BROWSER=${{ matrix.browser }}
WHATSAPP_DEFAULT_ENGINE=${{ matrix.engine }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+18 -18
View File
@@ -15,6 +15,7 @@ on:
jobs:
en:
if: github.repository == 'devlikeapro/waha'
permissions:
issues: write
discussions: write
@@ -31,21 +32,20 @@ jobs:
CUSTOM_BOT_NOTE: 'English'
TARGET_LANGUAGE: 'eng'
TRANSLATED_LANGUAGE: 'en'
pt:
permissions:
issues: write
discussions: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: devlikeapro/github-translate-action@main
env:
# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
IS_MODIFY_TITLE: false
CUSTOM_BOT_NOTE: 'Portuguese'
TARGET_LANGUAGE: 'por'
TRANSLATED_LANGUAGE: 'pt'
# pt:
# permissions:
# issues: write
# discussions: write
# pull-requests: write
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: devlikeapro/github-translate-action@main
# env:
# # GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# IS_MODIFY_TITLE: false
# CUSTOM_BOT_NOTE: 'Portuguese'
# TARGET_LANGUAGE: 'por'
# TRANSLATED_LANGUAGE: 'pt'
+2
View File
@@ -1,5 +1,7 @@
tmp/*
src/core/engines/gows/proto
.env
.envs/*
*.heapsnapshot
.yarn
example.ts
+1
View File
@@ -73,4 +73,5 @@ repos:
^src/core/engines/webjs/_lodash.js|
^tests/perf/.*|
^scripts/.*|
^scripts/init-waha.js|
)$
+26 -1
View File
@@ -1,5 +1,5 @@
ARG NODE_IMAGE_TAG=22.16-bookworm-slim
ARG GOLANG_IMAGE_TAG=1.23-bookworm
ARG GOLANG_IMAGE_TAG=1.24-bookworm
#
# Build
@@ -163,6 +163,7 @@ RUN if [ "$USE_BROWSER" = "chromium" ]; then \
# Available versions:
# https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable
ARG CHROME_VERSION="140.0.7339.80-1"
ARG OPUSTAGS_VERSION="1.10.1"
RUN if [ "$USE_BROWSER" = "chrome" ]; then \
wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \
&& apt-get update \
@@ -176,6 +177,22 @@ RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
# Build and install opustags so audio metadata can be cleaned up inside the container
RUN set -eux; \
buildDeps='build-essential cmake pkg-config libogg-dev'; \
apt-get update; \
apt-get install -y --no-install-recommends ${buildDeps}; \
mkdir -p /tmp/opustags; \
curl -L https://github.com/fmang/opustags/archive/refs/tags/${OPUSTAGS_VERSION}.tar.gz \
| tar -xz -C /tmp/opustags; \
cd /tmp/opustags/opustags-${OPUSTAGS_VERSION}; \
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release; \
cmake --build build --config Release; \
cmake --install build; \
rm -rf /tmp/opustags; \
apt-get purge -y --auto-remove ${buildDeps}; \
rm -rf /var/lib/apt/lists/*
# GOWS requirements
# libc6
RUN apt-get update \
@@ -195,6 +212,11 @@ COPY --from=build /git/node_modules ./node_modules
COPY --from=build /git/dist ./dist
COPY --from=dashboard /dashboard ./dist/dashboard
COPY --from=gows /go/gows/bin/gows /app/gows
COPY .env.example ./.env.example
COPY scripts/init-waha.js ./scripts/init-waha.js
RUN chmod +x ./scripts/init-waha.js \
&& printf '%s\n' '#!/bin/sh' 'exec node /app/scripts/init-waha.js "$@"' > /usr/local/bin/init-waha \
&& chmod +x /usr/local/bin/init-waha
ENV WAHA_GOWS_PATH=/app/gows
ENV WAHA_GOWS_SOCKET=/tmp/gows.sock
@@ -207,6 +229,9 @@ ENV CHOKIDAR_INTERVAL=5000
# WAHA variables
ENV WAHA_ZIPPER=ZIPUNZIP
# GOWS - use libc DNS resolver
ENV GODEBUG netdns=cgo
# Run command, etc
EXPOSE 3000
# Use tini as init system to handle zombie processes properly
+3 -3
View File
@@ -32,16 +32,16 @@ push:
docker push devlikeapro/waha
for-swagger:
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true WHATSAPP_SWAGGER_PASSWORD=666 . ${NVM_DIR}/nvm.sh && nvm exec yarn start
up-noweb:
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-10
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-21
up-noweb-libsignal:
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
up-webjs:
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05
start-proxy:
docker run --rm -d --name squid-container -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta
+1
View File
@@ -220,6 +220,7 @@ ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:
# ENABLE_RACK_ATTACK=true
# RACK_ATTACK_LIMIT=300
# ENABLE_RACK_ATTACK_WIDGET_API=true
CONVERSATION_MESSAGE_PER_MINUTE_LIMIT=3000
## Running chatwoot as an API only server
## setting this value to true will disable the frontend dashboard endpoints
+14 -14
View File
@@ -3,6 +3,20 @@
# https://waha.devlike.pro/docs/how-to/config/
#
# ====================
# ===== SECURITY =====
# ====================
WAHA_API_KEY_PLAIN=00000000000000000000000000000000
WAHA_API_KEY=00000000000000000000000000000000
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
# Disable Dashboard or Swagger
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# ==================
# ===== COMMON =====
# ==================
@@ -15,20 +29,6 @@ WAHA_PUBLIC_URL=http://localhost:3000
# WHATSAPP_API_PORT=3000
# WHATSAPP_API_HOSTNAME=localhost
# ====================
# ===== SECURITY =====
# ====================
# WAHA Apps requires both sha and plain version
WAHA_API_KEY_PLAIN=00000000000000000000000000000000
WAHA_API_KEY=sha512:98b6d128682e280b74b324ca82a6bae6e8a3f7174e0605bfd52eb9948fad8984854ec08f7652f32055c4a9f12b69add4850481d9503a7f2225501671d6124648
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=GOWS
+1 -1
View File
@@ -24,7 +24,7 @@ services:
- 8.8.8.8
base: &base
image: chatwoot/chatwoot:v4.6.0
image: chatwoot/chatwoot:v4.7.0
env_file: .chatwoot.env ## Change this file for customized env variables
volumes:
- chatwoot_storage:/app/storage
+10 -5
View File
@@ -41,11 +41,6 @@ if [ -n "$key" ]; then
# If already hashed, use it as is
export WAHA_API_KEY="$key"
else
# Display warning about using plain text API key
echo "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️"
echo "WARNING: Plain text API key detected. Converting to hashed format for security."
echo "For better security, use WAHA_API_KEY=sha512:{SHA512_HASH_FOR_YOUR_API_KEY}"
echo "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️"
# Hash the key using sha512sum
HASHED_KEY=$(echo -n "$key" | sha512sum | awk '{print $1}')
export WAHA_API_KEY="sha512:$HASHED_KEY"
@@ -75,6 +70,16 @@ else
fi
fi
#
# Enable CPU profiling
#
if [ "$WAHA_DEBUG_MODE" = "true" ] || [ "$WAHA_DEBUG_MODE" = "True" ] || [ "$WAHA_DEBUG_MODE" = "1" ]; then
case " $NODE_OPTIONS " in
*" --cpu-prof "*) ;;
*) export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--cpu-prof" ;;
esac
fi
#
# Start your application using node with exec to ensure proper signal handling
#
Binary file not shown.
Binary file not shown.
+13 -10
View File
@@ -26,7 +26,7 @@
"gows:proto": "yarn gows:proto:fetch && yarn gows:proto:build"
},
"dependencies": {
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-10",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-21",
"@adiwajshing/keyed-db": "^0.2.4",
"@aws-sdk/client-s3": "^3.633.0",
"@aws-sdk/s3-request-presigner": "^3.633.0",
@@ -59,29 +59,31 @@
"audio-decode": "^2.2.2",
"axios": "^1.9.0",
"axios-retry": "^4.5.0",
"better-sqlite3": "11.3.0",
"better-sqlite3": "^12.4.1",
"bullmq": "^5.48.1",
"check-disk-space": "^3.4.0",
"chokidar": "^3.6.0",
"class-transformer": "^0.5.1",
"class-validator": "0.14.0",
"commander": "^14.0.1",
"del": "^8.0.0",
"express-basic-auth": "^1.2.1",
"file-type": "16.5.4",
"fs-extra": "^11.2.0",
"google-protobuf": "^3.21.4",
"google-protobuf": "^4.0.1",
"https-proxy-agent": "^7.0.0",
"ical.js": "^2.2.1",
"ioredis": "^5",
"joi": "^17.13.3",
"knex": "^3.1.0",
"libphonenumber-js": "^1.10.36",
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"libsignal": "github:WhiskeySockets/libsignal-node#master",
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"long": "^5.3.2",
"mime-types": "^2.1.27",
"mongodb": "6.9.0",
"ms": "^2.1.3",
"mustache": "^4.2.0",
"nestjs-pino": "^4.1.0",
"node-cache": "5.1.2",
@@ -99,10 +101,12 @@
"rimraf": "^4.3.0",
"rxjs": "^7.8.1",
"sharp": "^0.33.4",
"shell-quote": "^1.8.3",
"sqlite3": "^5.1.7",
"swagger-ui-express": "^4.1.4",
"ulid": "^2.3.0",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10",
"undici": "^7.16.0",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05",
"write-file-atomic": "^6.0.0",
"yaml": "^2.7.1"
},
@@ -114,12 +118,11 @@
"ws": "^8.18.0",
"puppeteer": "^24.19.0",
"whatwg-url": "13.0.0",
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"axios": "^1.9.0"
},
"devDependencies": {
"@grpc/grpc-js": "^1.13.4",
"@grpc/proto-loader": "^0.7.13",
"@grpc/grpc-js": "^1.14.1",
"@grpc/proto-loader": "^0.8.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.0",
@@ -129,6 +132,7 @@
"@types/node-fetch": "^2.6.13",
"@types/semver": "^7.7.0",
"@types/supertest": "^2.0.8",
"dotenv": "^17.2.3",
"grpc-tools": "^1.13.0",
"jest": "^29.7.0",
"oxlint": "^1.13.0",
@@ -139,8 +143,7 @@
"ts-loader": "^6.2.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.0",
"typescript": "5.9.2",
"yargs": "^17.7.2"
"typescript": "5.9.2"
},
"jest": {
"moduleFileExtensions": [
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env node
const { readFileSync } = require('fs');
const { spawnSync } = require('child_process');
const path = require('path');
const YAML = require('yaml');
const repoRoot = path.resolve(__dirname, '..');
const enPath = path.join(repoRoot, 'src/apps/chatwoot/i18n/locales/en-US.yaml');
const contents = readFileSync(enPath, 'utf8');
const document = YAML.parse(contents);
const flatten = (node, prefix = '') => {
if (node && typeof node === 'object' && !Array.isArray(node)) {
return Object.entries(node).reduce((acc, [key, value]) => {
const next = prefix ? `${prefix}.${key}` : key;
return acc.concat(flatten(value, next));
}, []);
}
return [prefix];
};
const allKeys = flatten(document).filter(Boolean);
const ignoredPrefixes = ['datetime.', 'locale.'];
const ignored = new Set();
const unused = [];
const searchRoots = ['src', 'tests'];
for (const key of allKeys) {
if (
ignoredPrefixes.some((prefix) => key.startsWith(prefix)) ||
!key.includes('.')
) {
ignored.add(key);
continue;
}
const result = spawnSync(
'rg',
[
'--files-with-matches',
'--fixed-strings',
key,
...searchRoots,
'--glob',
'!apps/chatwoot/i18n/locales/*.yaml',
],
{
cwd: repoRoot,
encoding: 'utf8',
},
);
if (result.status !== 0 || !result.stdout.trim()) {
unused.push(key);
}
}
const message = [
`Scanned ${allKeys.length} keys.`,
`Ignored ${ignored.size} keys (datetime.*, locale.*, or without dot).`,
unused.length
? `Found ${unused.length} potentially unused keys:\n - ${unused.join(
'\n - ',
)}`
: 'No unused keys detected.',
];
console.log(message.join('\n'));
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const projectRoot = path.resolve(__dirname, '..');
const args = process.argv.slice(2);
const forceIndex = args.indexOf('--force');
const forceOverwrite = forceIndex !== -1;
if (forceOverwrite) {
args.splice(forceIndex, 1);
}
const folderArg = args[0] || '.';
const sourceArgRaw = args[1];
const targetArg = args[2] || '.env';
const envDir = path.resolve(process.cwd(), folderArg);
const sourceArg = sourceArgRaw || '.env.example';
const templateBaseDir = sourceArgRaw ? envDir : projectRoot;
const templatePath = path.isAbsolute(sourceArg)
? sourceArg
: path.resolve(templateBaseDir, sourceArg);
const targetPath = path.isAbsolute(targetArg)
? targetArg
: path.resolve(envDir, targetArg);
function exitWithMessage(message, code = 1) {
console.error(message);
process.exit(code);
}
if (!fs.existsSync(envDir)) {
exitWithMessage(`Target directory does not exist: ${envDir}`);
}
if (!fs.existsSync(templatePath)) {
exitWithMessage(`Missing template file: ${templatePath}`);
}
if (!forceOverwrite && fs.existsSync(targetPath)) {
const stats = fs.statSync(targetPath);
if (stats.size > 0) {
exitWithMessage(
`${targetPath} already exists and is not empty. Remove it manually or rerun with --force to overwrite.`,
);
}
}
const exampleContent = fs.readFileSync(templatePath, 'utf8');
function generateSecret() {
return crypto.randomUUID({ disableEntropyCache: true }).replace(/-/g, '');
}
const apiKey = generateSecret();
const adminPassword = generateSecret();
const updatedContent = exampleContent
.replace(/^WAHA_API_KEY=.*/m, `WAHA_API_KEY=${apiKey}`)
.replace(/^WAHA_API_KEY_PLAIN=.*/m, `WAHA_API_KEY_PLAIN=${apiKey}`)
.replace(/^WAHA_DASHBOARD_USERNAME=.*/m, 'WAHA_DASHBOARD_USERNAME=admin')
.replace(
/^WAHA_DASHBOARD_PASSWORD=.*/m,
`WAHA_DASHBOARD_PASSWORD=${adminPassword}`,
)
.replace(/^WHATSAPP_SWAGGER_USERNAME=.*/m, 'WHATSAPP_SWAGGER_USERNAME=admin')
.replace(
/^WHATSAPP_SWAGGER_PASSWORD=.*/m,
`WHATSAPP_SWAGGER_PASSWORD=${adminPassword}`,
);
fs.writeFileSync(targetPath, updatedContent, { encoding: 'utf8' });
const lines = [
'Credentials generated.',
'',
'Dashboard and Swagger:',
' - Username: admin',
` - Password: ${adminPassword}`,
'',
'API key: ',
` - ${apiKey}`,
'',
'Use it in the Dashboard connection flow: https://waha.devlike.pro/docs/how-to/dashboard/#api-key',
];
console.log(lines.join('\n'));
+7
View File
@@ -29,6 +29,7 @@ import {
GetChatMessageQuery,
GetChatMessagesFilter,
GetChatMessagesQuery,
MessageSortField,
OverviewBodyRequest,
OverviewFilter,
OverviewPaginationParams,
@@ -38,6 +39,7 @@ import {
transformAck,
} from '../structures/chats.dto';
import { EditMessageRequest } from '../structures/chatting.dto';
import { SortOrder } from '@waha/structures/pagination.dto';
@ApiSecurity('api_key')
@Controller('api/:session/chats')
@@ -120,6 +122,11 @@ class ChatsController {
@WorkingSessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
) {
if (query.sortBy == MessageSortField.MESSAGE_TIMESTAMP) {
query.sortBy = MessageSortField.TIMESTAMP;
}
query.sortBy = query.sortBy || MessageSortField.TIMESTAMP;
query.sortOrder = query.sortOrder || SortOrder.DESC;
filter = transformAck(filter);
return session.getChatMessages(chatId, query, filter);
}
+20
View File
@@ -44,6 +44,10 @@ import {
WANumberExistResult,
} from '../structures/chatting.dto';
import { WAMessage } from '../structures/responses.dto';
import {
mentionsAll,
validateRequestMentions,
} from '@waha/core/utils/mentions.all';
@ApiSecurity('api_key')
@Controller('api')
@@ -55,6 +59,10 @@ export class ChattingController {
@ApiOperation({ summary: 'Send a text message' })
async sendText(@Body() request: MessageTextRequest): Promise<WAMessage> {
const whatsapp = await this.manager.getWorkingSession(request.session);
if (mentionsAll(request)) {
validateRequestMentions(request);
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
}
return whatsapp.sendText(request);
}
@@ -66,6 +74,10 @@ export class ChattingController {
})
async sendImage(@Body() request: MessageImageRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
if (mentionsAll(request)) {
validateRequestMentions(request);
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
}
return whatsapp.sendImage(request);
}
@@ -77,6 +89,10 @@ export class ChattingController {
})
async sendFile(@Body() request: MessageFileRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
if (mentionsAll(request)) {
validateRequestMentions(request);
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
}
return whatsapp.sendFile(request);
}
@@ -99,6 +115,10 @@ export class ChattingController {
})
async sendVideo(@Body() request: MessageVideoRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
if (mentionsAll(request)) {
validateRequestMentions(request);
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
}
return whatsapp.sendVideo(request);
}
+12
View File
@@ -31,6 +31,7 @@ import {
CreateGroupRequest,
DescriptionRequest,
GroupField,
GroupParticipant,
GroupsListFields,
GroupsPaginationParams,
JoinGroupRequest,
@@ -320,6 +321,17 @@ export class GroupsController {
return session.getParticipants(id);
}
@Get(':id/participants/v2')
@GroupIdApiParam
@SessionApiParam
@ApiOperation({ summary: 'Get group participants.' })
getGroupParticipants(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<GroupParticipant[]> {
return session.getGroupParticipants(id);
}
@Post(':id/participants/add')
@HttpCode(HttpStatus.OK)
@SessionApiParam
-1
View File
@@ -2,7 +2,6 @@ import { Controller, Get } from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { PingResponse } from '@waha/structures/ping.dto';
@ApiSecurity('api_key')
@Controller('ping')
@ApiTags('🔍 Observability')
export class PingController {
+56 -1
View File
@@ -1,4 +1,6 @@
import * as inspector from 'node:inspector';
import * as v8 from 'node:v8';
import { Readable } from 'node:stream';
import {
Controller,
@@ -18,7 +20,10 @@ import {
WorkingSessionParam,
} from '@waha/nestjs/params/SessionApiParam';
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
import {
BrowserTraceQuery,
CpuProfileQuery,
} from '@waha/structures/server.debug.dto';
import { createReadStream } from 'fs';
@ApiSecurity('api_key')
@@ -36,6 +41,56 @@ export class ServerDebugController {
this.enabled = this.config.debugModeEnabled;
}
@Get('cpu')
@ApiOperation({
summary: 'Collect and return a CPU profile for the current nodejs process',
description: 'Uses the Node.js inspector profiler to capture a .cpuprofile',
})
@UsePipes(new WAHAValidationPipe())
async cpuProfile(@Query() query: CpuProfileQuery) {
if (!this.enabled) {
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
}
const { seconds } = query;
this.logger.log(`Collecting CPU profile for ${seconds}s...`);
const session = new inspector.Session();
session.connect();
const profile = await new Promise<inspector.Profiler.Profile>(
(resolve, reject) => {
session.post('Profiler.enable', (enableError) => {
if (enableError) {
session.disconnect();
return reject(enableError);
}
session.post('Profiler.start', (startError) => {
if (startError) {
session.disconnect();
return reject(startError);
}
setTimeout(() => {
session.post('Profiler.stop', (stopError, params) => {
session.disconnect();
if (stopError) {
return reject(stopError);
}
resolve(params.profile);
});
}, seconds * 1000);
});
});
},
);
const filename = `CPU.${Date.now()}.${process.pid}.cpuprofile`;
const stream = Readable.from([JSON.stringify(profile)]);
return new StreamableFile(stream, {
type: 'application/json',
disposition: `attachment; filename=${filename}`,
});
}
@Get('heapsnapshot')
@ApiOperation({
summary: 'Return a heapsnapshot for the current nodejs process',
+14
View File
@@ -7,6 +7,12 @@ import { DelayedError } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { Logger } from 'pino';
export interface JobDataTimeout {
timeout?: {
job?: number;
};
}
/**
* Base class for app consumers that provides common functionality
* like mutex locking, logging setup, and error handling.
@@ -27,6 +33,14 @@ export abstract class AppConsumer extends BaseWorkerHost {
});
}
protected signal(job) {
if (job.data?.timeout?.job != null) {
return AbortSignal.timeout(job.data?.timeout?.job);
}
// Aka never abort
return new AbortController().signal;
}
/**
* Executes a job with mutex locking to ensure only one job processes a specific resource at a time.
* @param job The job to process
+15 -6
View File
@@ -21,7 +21,7 @@ export class AxiosLogging {
: '';
const body = config.data ? JSON.stringify(config.data) : '';
this.logger.info(`${method} ${url}${query} ${body}`);
this.logger.debug(`${method} ${url}${query} ${body}`);
return config;
}
@@ -29,13 +29,22 @@ export class AxiosLogging {
const { method, url } = response.config;
const status = response.status;
const methodStr = method?.toUpperCase() ?? 'GET';
const responseData = JSON.stringify(response.data);
if (status >= 200 && status < 400) {
this.logger.info(`${methodStr} ${status}:OK ${url}`);
this.logger.debug(`${methodStr} ${status}:OK ${url} ${responseData}`);
const type: any = response.headers['content-type'];
// Log warnings for non-2xx/3xx responses
if (status < 200 || status >= 400) {
const data = JSON.stringify(response.data);
this.logger.warn(`${methodStr} ${status} ${url} ${data}`);
return response;
}
// Log debug for 2xx/3xx responses
this.logger.debug(`${methodStr} ${status}:OK ${url}`);
if (type.startsWith('application/json')) {
const data = JSON.stringify(response.data);
this.logger.trace(`${methodStr} ${status}:OK ${url} ${data}`);
} else {
this.logger.warn(`${methodStr} ${status} ${url} ${responseData}`);
this.logger.trace(`${methodStr} ${status}:OK ${url} [${type}]`);
}
return response;
+47 -6
View File
@@ -1,5 +1,5 @@
import { Job } from 'bullmq';
import { Backoffs } from 'bullmq';
import { Backoffs, Job } from 'bullmq';
import { FlowJob } from 'bullmq/dist/esm/interfaces';
/**
* Calculates the delay until the next attempt for a job.
@@ -51,6 +51,17 @@ export function HasBeenRetried(job: Job): boolean {
return attemptsMade > 1;
}
/**
* Hide chatwoot and waha from queue name for user faced messages
*/
export function QueueNameRepr(name: string): string {
// No "chatwoot" in the name always (it start with chatwoot)
name = name.replace('chatwoot.', '');
// Replace "waha" with "whatsapp"
name = name.replace('waha |', 'whatsapp |');
return name;
}
let base =
process.env.WAHA_PUBLIC_URL ||
process.env.WAHA_BASE_URL ||
@@ -59,9 +70,39 @@ let base =
base = base.replace(/\/+$/, '');
export function JobLink(job: Job): { text: string; url: string } {
const text = `${job.queueName} => ${job.id}`;
const url = `${base}/jobs/queue/${encodeURIComponent(job.queueName)}/${
job.id
}`;
// Use repr name for text
const name = QueueNameRepr(job.queueName);
const text = `${name} => ${job.id}`;
// Use original queue name in the URL
const queue = encodeURIComponent(job.queueName);
const id = encodeURIComponent(job.id);
const url = `${base}/jobs/queue/${queue}/${id}`;
return { text: text, url: url };
}
/**
* Chain jobs so that they run one at a time
* A | B | C
* to
* A -> B -> C
*/
export function ChainJobsOneAtATime(jobs: FlowJob[]): FlowJob {
if (jobs.length == 0) {
throw new Error('No jobs provided');
}
for (const job of jobs) {
if (job.children.length != 0) {
throw new Error('Jobs with children are not supported');
}
}
const left = [...jobs];
const root = left.pop();
let parent = root;
while (left.length != 0) {
const job = left.pop();
parent.children = [job];
parent = job;
}
return root;
}
+3 -2
View File
@@ -2,10 +2,11 @@ import * as process from 'node:process';
import { parseBool } from '@waha/helpers';
import * as basicAuth from 'express-basic-auth';
import { Auth } from '@waha/core/auth/config';
export function BullAuthMiddleware() {
let username = process.env.WAHA_DASHBOARD_USERNAME || '';
let password = process.env.WAHA_DASHBOARD_PASSWORD || '';
let username = Auth.dashboard.username.value || '';
let password = Auth.dashboard.password.value || '';
if (process.env.WAHA_DASHBOARD_ENABLED) {
const enabled = parseBool(process.env.WAHA_DASHBOARD_ENABLED);
if (!enabled) {
+43
View File
@@ -0,0 +1,43 @@
import * as lodash from 'lodash';
type Call<Record> = (processed: number) => Promise<Record[]>;
export interface PaginatorParams {
processed?: number;
max?: number;
}
const DefaultParams: PaginatorParams = {
processed: 0,
max: Infinity,
};
/**
* Generic paginator for APIs that return arrays of records with limits and offsets.
*/
export class ArrayPaginator<Record> {
private readonly params: PaginatorParams;
constructor(params: PaginatorParams = DefaultParams) {
this.params = lodash.merge({}, DefaultParams, params) as PaginatorParams;
}
async *iterate(call: Call<Record>): AsyncGenerator<Record> {
let processed = this.params.processed;
let records: Record[] = [];
while (true) {
records = await call(processed);
if (records.length === 0) {
return;
}
for (const record of records) {
yield record;
processed += 1;
if (processed >= this.params.max) {
return;
}
}
}
}
}
+462
View File
@@ -0,0 +1,462 @@
import { Channel } from '@waha/structures/channels.dto';
import {
GetChatMessagesFilter,
GetChatMessagesQuery,
} from '@waha/structures/chats.dto';
import {
ChatRequest,
MessageFileRequest,
MessageImageRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
WANumberExistResult,
} from '@waha/structures/chatting.dto';
import { SessionInfo } from '@waha/structures/sessions.dto';
import axios, { AxiosInstance } from 'axios';
import { Auth } from '@waha/core/auth/config';
import { ContactSortField } from '@waha/structures/contacts.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
export interface RequestOptions {
signal?: AbortSignal;
}
export class WAHASelf {
public client: AxiosInstance;
constructor() {
// Set 'X-Api-Key'
const key = Auth.keyplain.value;
const port =
parseInt(process.env.PORT) ||
parseInt(process.env.WHATSAPP_API_PORT) ||
3000;
const url = `http://localhost:${port}`;
this.client = axios.create({
baseURL: url,
headers: {
'X-Api-Key': key,
'Content-Type': 'application/json',
},
});
}
async fetch(url: string, opts?: RequestOptions): Promise<Buffer> {
const response = await this.client.get(url, {
responseType: 'arraybuffer',
signal: opts?.signal,
});
return Buffer.from(response.data);
}
async qr(session: string, opts?: RequestOptions): Promise<Buffer> {
const url = `/api/${session}/auth/qr`;
return await this.fetch(url, opts);
}
async screenshot(session: string, opts?: RequestOptions): Promise<Buffer> {
const url = `/api/screenshot?session=${session}`;
return await this.fetch(url, opts);
}
async restart(session: string, opts?: RequestOptions): Promise<any> {
const url = `/api/sessions/${session}/restart`;
return await this.client.post(url, undefined, { signal: opts?.signal });
}
async logout(session: string, opts?: RequestOptions): Promise<any> {
const url = `/api/sessions/${session}/logout`;
return await this.client.post(url, undefined, { signal: opts?.signal });
}
async stop(session: string, opts?: RequestOptions): Promise<any> {
const url = `/api/sessions/${session}/stop`;
return await this.client.post(url, undefined, { signal: opts?.signal });
}
async get(session: string, opts?: RequestOptions): Promise<SessionInfo> {
const url = `/api/sessions/${session}/`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data);
}
async getChats(
session: string,
page: PaginationParams,
opts?: RequestOptions,
) {
const url = `/api/${session}/chats`;
const params = {
...page,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async getContacts(
session: string,
page: PaginationParams,
opts?: RequestOptions,
) {
const url = `/api/contacts/all`;
const params = {
session: session,
...page,
sortBy: ContactSortField.ID,
sortOrder: ContactSortField.ID,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async getContact(session: string, contactId: string, opts?: RequestOptions) {
const url = `/api/contacts`;
const params = {
session: session,
contactId: contactId,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async contactCheckExists(
session: string,
phone: string,
opts?: RequestOptions,
): Promise<WANumberExistResult> {
const url = `/api/contacts/check-exists`;
const params = {
phone: phone,
session: session,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async getGroup(session: string, groupId: string, opts?: RequestOptions) {
const url = `/api/${session}/groups/${groupId}`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data);
}
async getChannel(
session: string,
channelId: string,
opts?: RequestOptions,
): Promise<Channel> {
const url = `/api/${session}/channels/${channelId}`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data);
}
async getChatPicture(
session: string,
chatId: string,
opts?: RequestOptions,
): Promise<string | null> {
const url = `/api/${session}/chats/${chatId}/picture`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data?.url);
}
async sendText(
body: MessageTextRequest,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/sendText`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async sendImage(
body: MessageImageRequest,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/sendImage`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async sendVideo(
body: MessageVideoRequest,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/sendVideo`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async sendVoice(
body: MessageVoiceRequest,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/sendVoice`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async sendFile(
body: MessageFileRequest,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/sendFile`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async deleteMessage(
session: string,
chatId: string,
messageId: string,
opts?: RequestOptions,
) {
const url = `/api/${session}/chats/${chatId}/messages/${messageId}`;
return await this.client
.delete(url, { signal: opts?.signal })
.then((response) => response.data);
}
async startTyping(body: ChatRequest, opts?: RequestOptions) {
const url = `/api/startTyping`;
return await this.client
.post(url, body, { signal: opts?.signal })
.then((response) => response.data);
}
async stopTyping(body: ChatRequest, opts?: RequestOptions) {
const url = `/api/stopTyping`;
return await this.client.post(url, body, { signal: opts?.signal });
}
async readMessages(session: string, chatId: string, opts?: RequestOptions) {
const url = `/api/${session}/chats/${chatId}/messages/read`;
return await this.client
.post(url, undefined, { signal: opts?.signal })
.then((response) => response.data);
}
async getMessages(
session: string,
chatId: string,
query: GetChatMessagesQuery,
filter: GetChatMessagesFilter,
opts?: RequestOptions,
) {
const url = `/api/${session}/chats/${chatId}/messages`;
const params = {
...query,
...filter,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async getMessageById(
session: string,
chatId: string,
messageId: string,
media: boolean,
opts?: RequestOptions,
) {
const url = `/api/${session}/chats/${chatId}/messages/${messageId}`;
const params = {
downloadMedia: media,
};
return await this.client
.get(url, { params: params, signal: opts?.signal })
.then((response) => response.data);
}
async findPNByLid(
session: string,
lid: string,
opts?: RequestOptions,
): Promise<string | null> {
const url = `/api/${session}/lids/${lid}`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data.pn);
}
async findLIDByPN(
session: string,
pn: string,
opts?: RequestOptions,
): Promise<string | null> {
const url = `/api/${session}/lids/pn/${pn}`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data.lid);
}
/**
* Get the server version
* @returns Server version information
*/
async serverVersion(opts?: RequestOptions): Promise<any> {
const url = `/api/server/version`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data);
}
/**
* Get the server status
* @returns Server status information
*/
async serverStatus(opts?: RequestOptions): Promise<any> {
const url = `/api/server/status`;
return await this.client
.get(url, { signal: opts?.signal })
.then((response) => response.data);
}
/**
* Reboot the server
* @param force Whether to force reboot (true) or gracefully reboot (false)
* @returns Server stop response
*/
async serverReboot(
force: boolean = false,
opts?: RequestOptions,
): Promise<any> {
const url = `/api/server/stop`;
return await this.client
.post(url, { force }, { signal: opts?.signal })
.then((response) => response.data);
}
}
export class WAHASessionAPI {
constructor(
private session: string,
private api: WAHASelf,
) {}
getChats(page: PaginationParams, opts?: RequestOptions) {
return this.api.getChats(this.session, page, opts);
}
getContacts(page: PaginationParams, opts?: RequestOptions): Promise<any> {
return this.api.getContacts(this.session, page, opts);
}
getContact(contactId: string, opts?: RequestOptions): Promise<any> {
return this.api.getContact(this.session, contactId, opts);
}
contactCheckExists(
phone: string,
opts?: RequestOptions,
): Promise<WANumberExistResult> {
return this.api.contactCheckExists(this.session, phone, opts);
}
getGroup(groupId: string, opts?: RequestOptions): Promise<any> {
return this.api.getGroup(this.session, groupId, opts);
}
getChannel(channelId: string, opts?: RequestOptions): Promise<Channel> {
return this.api.getChannel(this.session, channelId, opts);
}
getChatPicture(
chatId: string,
opts?: RequestOptions,
): Promise<string | null> {
return this.api.getChatPicture(this.session, chatId, opts);
}
sendText(body: MessageTextRequest, opts?: RequestOptions): Promise<any> {
body.session = this.session;
return this.api.sendText(body, opts);
}
sendImage(body: MessageImageRequest, opts?: RequestOptions): Promise<any> {
body.session = this.session;
return this.api.sendImage(body, opts);
}
sendVideo(body: MessageVideoRequest, opts?: RequestOptions): Promise<any> {
body.session = this.session;
return this.api.sendVideo(body, opts);
}
sendVoice(body: MessageVoiceRequest, opts?: RequestOptions): Promise<any> {
body.session = this.session;
return this.api.sendVoice(body, opts);
}
sendFile(body: MessageFileRequest, opts?: RequestOptions): Promise<any> {
body.session = this.session;
return this.api.sendFile(body, opts);
}
deleteMessage(chatId: string, messageId: string, opts?: RequestOptions) {
return this.api.deleteMessage(this.session, chatId, messageId, opts);
}
startTyping(body: ChatRequest, opts?: RequestOptions) {
body.session = this.session;
return this.api.startTyping(body, opts);
}
stopTyping(body: ChatRequest, opts?: RequestOptions) {
body.session = this.session;
return this.api.stopTyping(body, opts);
}
readMessages(chatId: string, opts?: RequestOptions) {
return this.api.readMessages(this.session, chatId, opts);
}
async getMessages(
chatId: string,
query: GetChatMessagesQuery,
filter: GetChatMessagesFilter,
opts?: RequestOptions,
) {
return this.api.getMessages(this.session, chatId, query, filter, opts);
}
async getMessageById(
chatId: string,
messageId: string,
media: boolean,
opts?: RequestOptions,
) {
return this.api.getMessageById(
this.session,
chatId,
messageId,
media,
opts,
);
}
//
// Lids
//
findPNByLid(lid: string, opts?: RequestOptions) {
return this.api.findPNByLid(this.session, lid, opts);
}
findLIDByPN(pn: string, opts?: RequestOptions) {
return this.api.findLIDByPN(this.session, pn, opts);
}
}
+3 -2
View File
@@ -13,6 +13,7 @@ import { ChatwootLocalesController } from '@waha/apps/chatwoot/api/chatwoot.loca
import { ChatWootExports } from '@waha/apps/chatwoot/chatwoot.module';
import { parseBool } from '@waha/helpers';
import { RMutexModule } from '@waha/modules/rmutex';
import { Auth } from '@waha/core/auth/config';
const IMPORTS = [
BullModule.forRoot({
@@ -93,11 +94,11 @@ const AppsDisabled = {
};
function checkApiKey() {
const key = process.env.WHATSAPP_API_KEY || process.env.WAHA_API_KEY;
const key = Auth.key.value;
if (!key) {
return;
}
const plain = process.env.WAHA_API_KEY_PLAIN;
const plain = Auth.keyplain.value;
if (!plain) {
throw Error(
'WAHA_API_KEY set, please provide WAHA_API_KEY_PLAIN when WAHA_APPS_ENABLED',
-18
View File
@@ -1,18 +0,0 @@
import { WAHASessionStatus } from '@waha/structures/enums.dto';
export function SessionStatusEmoji(status: WAHASessionStatus): string {
switch (status) {
case WAHASessionStatus.STOPPED:
return '⚠️';
case WAHASessionStatus.STARTING:
return '⏳';
case WAHASessionStatus.SCAN_QR_CODE:
return '⚠️';
case WAHASessionStatus.WORKING:
return '🟢';
case WAHASessionStatus.FAILED:
return '🛑';
default:
return '❓';
}
}
@@ -6,13 +6,13 @@ import {
Post,
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { FindChatID } from '@waha/apps/chatwoot/client/ids';
import { IsCommandsChat } 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';
import { CommandPrefix } from '@waha/apps/chatwoot/cli';
@Controller('webhooks/chatwoot/')
@ApiTags('🧩 Apps')
@@ -36,16 +36,26 @@ export class ChatwootWebhookController {
return { success: true };
}
// Ignore all private notes
if (body.private) {
return { success: true };
}
// Ignore all incoming messages
if (body.message_type == MessageType.INCOMING) {
return { success: true };
}
const isCommandsChat = IsCommandsChat(body);
// Ignore private notes (most of them)
const deleted = body?.content_attributes?.deleted;
if (body.private) {
// Ignore any private note in commands chats
if (isCommandsChat) {
return { success: true };
}
// keep "deleted" notes
// So Agent can delete "Sent From API/WhatsApp" messages in ChatWoot
if (!deleted) {
return { success: true };
}
}
const data: InboxData = {
session: session,
app: id,
@@ -61,12 +71,8 @@ export class ChatwootWebhookController {
}
// Check if it's a command message (sent to the special inbox contact)
const sender = body?.conversation?.meta?.sender;
const chatId = FindChatID(sender);
const isCommandsChat = chatId === INBOX_CONTACT_CHAT_ID;
// Check if it's a deleted message
if (body.content_attributes?.deleted && !isCommandsChat) {
if (deleted && !isCommandsChat) {
await this.chatWootQueueService.addMessageDeletedJob(data);
return { success: true };
}
@@ -74,10 +80,10 @@ export class ChatwootWebhookController {
// Route to specific queues based on an event type
switch (body.event) {
case EventName.MESSAGE_CREATED:
if (!isCommandsChat) {
await this.chatWootQueueService.addMessageCreatedJob(data);
} else {
if (isCommandsChat || body.content?.startsWith(CommandPrefix)) {
await this.chatWootQueueService.addCommandsJob(body.event, data);
} else {
await this.chatWootQueueService.addMessageCreatedJob(data);
}
return { success: true };
case EventName.MESSAGE_UPDATED:
@@ -94,10 +100,10 @@ export class ChatwootWebhookController {
return { success: true };
}
if (!isCommandsChat) {
await this.chatWootQueueService.addMessageUpdatedJob(data);
} else {
if (isCommandsChat || body.content?.startsWith(CommandPrefix)) {
await this.chatWootQueueService.addCommandsJob(body.event, data);
} else {
await this.chatWootQueueService.addMessageUpdatedJob(data);
}
return { success: true };
default:
+25 -1
View File
@@ -15,7 +15,7 @@ import { ChatWootInboxCommandsConsumer } from './consumers/inbox/commands';
import { ChatWootInboxMessageCreatedConsumer } from './consumers/inbox/message_created';
import { ChatWootInboxMessageDeletedConsumer } from './consumers/inbox/message_deleted';
import { ChatWootInboxMessageUpdatedConsumer } from './consumers/inbox/message_updated';
import { QueueName } from './consumers/QueueName';
import { FlowProducerName, QueueName } from './consumers/QueueName';
import { CheckVersionConsumer } from './consumers/scheduled/check.version';
import { WAHAMessageAnyConsumer } from './consumers/waha/message.any';
import { WAHAMessageEditedConsumer } from './consumers/waha/message.edited';
@@ -26,12 +26,20 @@ import { WAHASessionStatusConsumer } from './consumers/waha/session.status';
import { ChatWootQueueService } from './services/ChatWootQueueService';
import { ChatWootScheduleService } from './services/ChatWootScheduleService';
import { ChatWootWAHAQueueService } from './services/ChatWootWAHAQueueService';
import { QueueRegistry } from './services/QueueRegistry';
import { ChatWootConversationCreatedConsumer } from './consumers/inbox/conversation_created';
import { ChatWootConversationStatusChangedConsumer } from '@waha/apps/chatwoot/consumers/inbox/conversation_status_changed';
import { TaskContactsPullConsumer } from '@waha/apps/chatwoot/consumers/task/contacts.pull';
import { TaskMessagesPullConsumer } from '@waha/apps/chatwoot/consumers/task/messages.pull';
import { BullModule } from '@nestjs/bullmq';
import { QueueManager } from '@waha/apps/chatwoot/services/QueueManager';
const CONTROLLERS = [ChatwootWebhookController, ChatwootLocalesController];
const IMPORTS = lodash.flatten([
BullModule.registerFlowProducer({
name: FlowProducerName.MESSAGES_PULL_FLOW,
}),
RegisterAppQueue({
name: QueueName.SCHEDULED_MESSAGE_CLEANUP,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
@@ -40,6 +48,14 @@ const IMPORTS = lodash.flatten([
name: QueueName.SCHEDULED_CHECK_VERSION,
defaultJobOptions: merge(NoRetriesJobOptions, JobRemoveOptions),
}),
RegisterAppQueue({
name: QueueName.TASK_CONTACTS_PULL,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
}),
RegisterAppQueue({
name: QueueName.TASK_MESSAGES_PULL,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
}),
RegisterAppQueue({
name: QueueName.WAHA_MESSAGE_ANY,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
@@ -98,18 +114,26 @@ const PROVIDERS = [
ChatWootConversationCreatedConsumer,
ChatWootConversationStatusChangedConsumer,
ChatWootInboxCommandsConsumer,
// Tasks
TaskContactsPullConsumer,
TaskMessagesPullConsumer,
// WAHA
WAHASessionStatusConsumer,
WAHAMessageAnyConsumer,
WAHAMessageReactionConsumer,
WAHAMessageEditedConsumer,
WAHAMessageRevokedConsumer,
WAHAMessageAckConsumer,
// Scheduled
MessageCleanupConsumer,
CheckVersionConsumer,
// Services
ChatWootWAHAQueueService,
ChatWootQueueService,
ChatWootScheduleService,
ChatWootAppService,
QueueRegistry,
QueueManager,
];
export const ChatWootExports = {
+80
View File
@@ -0,0 +1,80 @@
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import {
ContactsPullOptions,
ContactsPullStatusMessage,
} from '@waha/apps/chatwoot/consumers/task/contacts.pull';
import { Job, JobsOptions, Queue } from 'bullmq';
import { ChatWootScheduleService } from '@waha/apps/chatwoot/services/ChatWootScheduleService';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { JobDataTimeout } from '@waha/apps/app_sdk/AppConsumer';
import {
ExponentialRetriesJobOptions,
JobRemoveOptions,
merge,
} from '@waha/apps/app_sdk/constants';
export async function ContactsPullStart(
ctx: CommandContext,
options: ContactsPullOptions,
jobOptions: JobsOptions & JobDataTimeout,
) {
const jobId = ChatWootScheduleService.SingleJobId(ctx.data.app);
const job: Job = await ctx.queues.contactsPull.getJob(jobId);
if (job) {
const state = await job.getState();
const done = state === 'completed' || state === 'failed';
if (!done) {
const msg = ctx.l.r('cli.cmd.contacts.pull.already-running');
await ctx.conversation.incoming(msg);
return;
}
// Remove already done job
await ContactsPullRemove(ctx.queues.contactsPull, ctx.data.app, ctx.logger);
}
const opts: JobsOptions = merge(
ExponentialRetriesJobOptions,
JobRemoveOptions,
jobOptions,
);
const data = {
...ctx.data,
timeout: {
job: jobOptions.timeout.job,
},
options: options,
};
opts.jobId = jobId;
await ctx.queues.contactsPull.add(QueueName.TASK_CONTACTS_PULL, data, opts);
}
export async function ContactsPullStatus(ctx: CommandContext) {
const jobId = ChatWootScheduleService.SingleJobId(ctx.data.app);
const job: Job | null = await ctx.queues.contactsPull.getJob(jobId);
if (!job) {
const msg = ctx.l.r('cli.cmd.contacts.status.not-found');
await ctx.conversation.incoming(msg);
return;
}
const state = await job.getState();
const msg = ContactsPullStatusMessage(ctx.l, job, state);
await ctx.conversation.incoming(msg);
}
export async function ContactsPullRemove(
queue: Queue,
app: string,
logger: ILogger,
): Promise<boolean> {
const jobId = ChatWootScheduleService.SingleJobId(app);
const job: Job = await queue.getJob(jobId);
if (!job) {
logger.info('Pull Contacts job has already been removed');
return false;
}
await job.remove();
return true;
}
+7
View File
@@ -0,0 +1,7 @@
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
export async function CommandDisabled(ctx: CommandContext, command: string) {
const text = ctx.l.r('cli.cmd.disabled', { command: command });
await ctx.conversation.incoming(text);
}
+182
View File
@@ -0,0 +1,182 @@
import * as lodash from 'lodash';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import {
ChatID,
MessagesPullOptions,
MessagesPullStatusMessage,
TaskActivity,
} from '@waha/apps/chatwoot/consumers/task/messages.pull';
import { ChatWootScheduleService } from '@waha/apps/chatwoot/services/ChatWootScheduleService';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { JobDataTimeout } from '@waha/apps/app_sdk/AppConsumer';
import { FlowProducer, Job, JobsOptions, Queue } from 'bullmq';
import { FlowJob } from 'bullmq/dist/esm/interfaces';
import { GetAllChatIDs, IsCommandsChat } from '@waha/apps/chatwoot/client/ids';
import { ChainJobsOneAtATime } from '@waha/apps/app_sdk/JobUtils';
import {
ExponentialRetriesJobOptions,
JobRemoveOptions,
merge,
} from '@waha/apps/app_sdk/constants';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
import { WAHASelf, WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import {
IgnoreJidConfig,
isNullJid,
isPnUser,
JidFilter,
} from '@waha/core/utils/jids';
function oneJob(chat: string, data, opts: JobsOptions): FlowJob {
data = lodash.cloneDeep(data);
data.options.chat = chat;
opts = lodash.cloneDeep(opts);
return {
name: chat,
queueName: QueueName.TASK_MESSAGES_PULL,
data: data,
opts: merge(ExponentialRetriesJobOptions, JobRemoveOptions, opts),
children: [],
};
}
export async function MessagesPullStart(
ctx: CommandContext,
options: MessagesPullOptions,
jobOptions: JobsOptions & JobDataTimeout,
) {
const queue = ctx.queues.messagesPull;
const jobId = ChatWootScheduleService.SingleJobId(ctx.data.app);
const job: Job | null = await queue.getJob(jobId);
if (job) {
const state = await job.getState();
const done = state === 'completed' || state === 'failed';
if (!done) {
const msg = ctx.l.r('cli.cmd.messages.pull.already-running', {
chat: job.data.options.chat,
});
await ctx.conversation.incoming(msg);
return;
}
await MessagesPullRemove(queue, ctx.data.app, ctx.logger);
}
const opts: JobsOptions = {
...jobOptions,
ignoreDependencyOnFailure: true,
delay: 1_000,
};
const data = {
...ctx.data,
timeout: {
job: jobOptions.timeout.job,
},
options: options,
};
const producer: FlowProducer = ctx.flows.messagesPull;
let chats: string[] = [options.chat];
if (options.chat === ChatID.ALL) {
if (IsCommandsChat(data.body)) {
if (EngineHelper.SupportsAllChatForMessage()) {
// keep "all" chats - that's fine
} else {
// "all" chats for engine that doesn't support it
chats = await resolveAllToChats(ctx.waha, ctx.data.session, options);
if (chats.length == 0) {
throw new Error(ctx.l.r('cli.cmd.messages.pull.no-chats-found'));
}
}
} else {
// It's per-chat command, get the chat ids and run the pulling process
chats = GetAllChatIDs(data.body?.conversation?.meta?.sender);
chats = EngineHelper.FilterChatIdsForMessages(chats);
}
}
const children = chats.map((chat) => oneJob(chat, data, opts));
let root: FlowJob;
if (children.length == 1) {
// Schedule one job, no "summary" job required
root = children[0];
} else {
// Add "summary" parent job so we get the overall progress at the end
root = oneJob(ChatID.SUMMARY, data, opts);
root.children = [ChainJobsOneAtATime(children)];
}
root.opts.jobId = jobId;
await producer.add(root);
const activity = new TaskActivity(ctx.l, ctx.conversation);
await activity.details(data);
}
export async function MessagesPullStatus(ctx: CommandContext) {
const queue = ctx.queues.messagesPull;
const jobId = ChatWootScheduleService.SingleJobId(ctx.data.app);
const job: Job | null = await queue.getJob(jobId);
if (!job) {
const msg = ctx.l.r('cli.cmd.messages.status.not-found');
await ctx.conversation.incoming(msg);
return;
}
const state = await job.getState();
const msg = MessagesPullStatusMessage(ctx.l, job, state);
await ctx.conversation.incoming(msg);
}
export async function MessagesPullRemove(
queue: Queue,
app: string,
logger: ILogger,
): Promise<boolean> {
const jobId = ChatWootScheduleService.SingleJobId(app);
const job: Job | null = await queue.getJob(jobId);
if (!job) {
logger.info('Pull Messages job has already been removed');
return false;
}
await job.remove();
return true;
}
/**
* WORKING ONLY FOR WEBJS
*/
async function resolveAllToChats(
waha: WAHASelf,
sessionName: string,
options: MessagesPullOptions,
): Promise<string[]> {
const session = new WAHASessionAPI(sessionName, waha);
let chats = await session.getChats({
limit: undefined,
offset: undefined,
});
// new chat last
chats = lodash.sortBy(chats, (c) => c.timestamp || 0);
const gte = Date.now() - options.period.start;
const result = [];
const jids = new JidFilter(options.ignore);
for (const chat of chats) {
const id = chat.id._serialized;
if (!jids.include(id)) {
continue;
}
if (isNullJid(id)) {
continue;
}
const timestamp = (chat.timestamp || Infinity) * 1000;
if (timestamp < gte) {
// Too old chat
continue;
}
result.push(id);
}
return result;
}
+45
View File
@@ -0,0 +1,45 @@
import * as lodash from 'lodash';
import { QueueManager } from '@waha/apps/chatwoot/services/QueueManager';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { QueueRegistry } from '@waha/apps/chatwoot/services/QueueRegistry';
import { QueueNameRepr } from '@waha/apps/app_sdk/JobUtils';
export interface QueueCommandContext {
queues: {
registry: QueueRegistry;
};
l: Locale;
conversation: Conversation;
}
export async function QueueStatus(ctx: QueueCommandContext, name: string) {
const manager = new QueueManager(ctx.queues.registry);
const names = manager.resolve(name);
let result = await manager.status(names);
for (const status of result) {
status.name = QueueNameRepr(status.name);
}
// locked: true - last
result = lodash.sortBy(result, [(x) => !!x.locked, 'name']);
const msg = ctx.l.r('cli.cmd.queue.status.result', {
queues: result,
});
await ctx.conversation.incoming(msg);
}
export async function QueueStart(ctx: QueueCommandContext, name: string) {
const manager = new QueueManager(ctx.queues.registry);
const names = manager.resolve(name);
await manager.resume(names);
const msg = ctx.l.r('cli.cmd.queue.resumed');
await ctx.conversation.activity(msg);
}
export async function QueueStop(ctx: QueueCommandContext, name?: string) {
const manager = new QueueManager(ctx.queues.registry);
const names = manager.resolve(name);
await manager.pause(names);
const msg = ctx.l.r('cli.cmd.queue.paused');
await ctx.conversation.activity(msg);
}
+22
View File
@@ -0,0 +1,22 @@
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
export async function ServerStatus(ctx: CommandContext) {
ctx.logger.info('Getting server version and status');
const version = await ctx.waha.serverVersion();
const status = await ctx.waha.serverStatus();
const text = ctx.l.key(TKey.APP_SERVER_VERSION_AND_STATUS).render({
version: JSON.stringify(version, null, 2),
status: JSON.stringify(status, null, 2),
});
await ctx.conversation.incoming(text);
}
export async function ServerReboot(ctx: CommandContext, force: boolean) {
const type = force ? 'force' : 'graceful';
const tkey = force ? TKey.APP_SERVER_REBOOT_FORCE : TKey.APP_SERVER_REBOOT;
ctx.logger.info(`Rebooting server (${type})`);
const text = ctx.l.key(tkey).render();
await ctx.conversation.incoming(text);
await ctx.waha.serverReboot(false);
}
+54
View File
@@ -0,0 +1,54 @@
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { SessionStatusEmoji } from '@waha/apps/chatwoot/emoji';
import { AttachmentFromBuffer } from '@waha/apps/chatwoot/client/messages';
import { MessageType } from '@waha/apps/chatwoot/client/types';
export async function SessionRestart(ctx: CommandContext) {
await ctx.waha.restart(ctx.data.session);
}
export async function SessionStart(ctx: CommandContext) {
return SessionRestart(ctx);
}
export async function SessionLogout(ctx: CommandContext) {
ctx.logger.info(`Logging out session ${ctx.data.session}`);
await ctx.waha.logout(ctx.data.session);
const text = ctx.l.key(TKey.APP_LOGOUT_SUCCESS).render();
await ctx.conversation.incoming(text);
}
export async function SessionStop(ctx: CommandContext) {
ctx.logger.info(`Stopping session ${ctx.data.session}`);
await ctx.waha.stop(ctx.data.session);
}
export async function SessionStatus(ctx: CommandContext) {
ctx.logger.info(`Getting status for session ${ctx.data.session}`);
const session = await ctx.waha.get(ctx.data.session);
const emoji = SessionStatusEmoji(session.status);
const text = ctx.l.key(TKey.APP_SESSION_CURRENT_STATUS).render({
emoji: emoji,
session: session.name,
status: session.status,
name: session.me?.pushName || 'Unknown',
id: session.me?.id || 'No phone number',
});
await ctx.conversation.incoming(text);
}
export async function SessionQR(ctx: CommandContext) {
const content = await ctx.waha.qr(ctx.data.session);
const message = AttachmentFromBuffer(content, 'qr.jpg');
message.message_type = MessageType.INCOMING;
await ctx.conversation.send(message);
}
export async function SessionScreenshot(ctx: CommandContext) {
ctx.logger.info(`Getting screenshot for session ${ctx.data.session}`);
const content = await ctx.waha.screenshot(ctx.data.session);
const message = AttachmentFromBuffer(content, 'screenshot.jpg');
message.message_type = MessageType.INCOMING;
await ctx.conversation.send(message);
}
+35
View File
@@ -0,0 +1,35 @@
import { CommanderError } from 'commander';
import { parse } from 'shell-quote';
import { BufferedOutput } from '@waha/apps/chatwoot/cli/utils/BufferedOutput';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { BuildProgram } from '@waha/apps/chatwoot/cli/program.a';
import { CHATWOOT_COMMAND_PREFIX } from '@waha/apps/chatwoot/env';
export async function runText(
commands: ChatWootCommandsConfig,
ctx: CommandContext,
text: string,
) {
// Prepare text for cli like command
text = text
.split('\n') // split into lines
.map((line) => line.trimStart()) // remove indentation
.join(' ') // join with space
.trim(); // final cleanup
const output = new BufferedOutput();
const program = BuildProgram(commands, ctx, output);
const argv = parse(text);
try {
await program.parseAsync(argv, { from: 'user' });
} catch (err) {
if (err instanceof CommanderError) {
return output;
} else {
throw err; // unexpected bug in your action code
}
}
return output;
}
export const CommandPrefix = CHATWOOT_COMMAND_PREFIX;
+70
View File
@@ -0,0 +1,70 @@
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { Argument, Command, Option, OutputConfiguration } from 'commander';
import {
buildFormatHelp,
fullCommandPath,
} from '@waha/apps/chatwoot/cli/utils/help';
import { AddSessionCommand } from '@waha/apps/chatwoot/cli/program.session';
import { AddContactsCommand } from '@waha/apps/chatwoot/cli/program.contacts';
import { AddMessagesCommand } from '@waha/apps/chatwoot/cli/program.messages';
import { AddServerCommand } from '@waha/apps/chatwoot/cli/program.server';
import { AddQueueCommand } from '@waha/apps/chatwoot/cli/program.queue';
function Program(ctx: CommandContext, output: OutputConfiguration) {
const l = ctx.l;
const program = new Command();
program
.name('')
.description(l.r('cli.cmd.root.description'))
.exitOverride()
.configureOutput(output)
.showSuggestionAfterError(true)
.configureHelp({
helpWidth: 200,
styleOptionTerm(str: string) {
return `- \`${str}\``;
},
styleArgumentTerm(str: string) {
return `- **${str}**`;
},
styleUsage(str) {
return `**${str}**`;
},
styleSubcommandTerm(str: string) {
return `- **${str}**`;
},
styleSubcommandDescription(str: string) {
return str ? `- ${str}` : str;
},
subcommandTerm: fullCommandPath,
commandUsage(cmd) {
// For the root program - no usage
if (!cmd.parent)
return `${l.r('cli.usage.command')} ${l.r('cli.usage.options')}`;
// For subcommands, keep default behavior
if (cmd.commands.length > 0) {
return `${fullCommandPath(cmd)} ${l.r('cli.usage.command')} ${l.r(
'cli.usage.options',
)}`;
}
return `${fullCommandPath(cmd)} ${l.r('cli.usage.options')}`;
},
formatHelp: buildFormatHelp(l),
});
return program;
}
export function BuildProgram(
commands: ChatWootCommandsConfig,
ctx: CommandContext,
output: OutputConfiguration,
) {
const program = Program(ctx, output);
AddSessionCommand(program, ctx);
AddContactsCommand(program, ctx);
AddMessagesCommand(program, ctx, commands.queue);
AddQueueCommand(program, ctx, commands.queue);
AddServerCommand(program, ctx, commands.server);
return program;
}
+113
View File
@@ -0,0 +1,113 @@
import { Argument, Command, Option } from 'commander';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import {
JobAttemptsOption,
JobTimeoutOption,
ParseMS,
NotNegativeNumber,
ProgressOption,
} from '@waha/apps/chatwoot/cli/utils/options';
import {
ContactsPullStart,
ContactsPullStatus,
} from '@waha/apps/chatwoot/cli/cmd.contacts';
import { ContactsPullOptions } from '@waha/apps/chatwoot/consumers/task/contacts.pull';
import { JobsOptions } from 'bullmq';
import { JobDataTimeout } from '@waha/apps/app_sdk/AppConsumer';
export function AddContactsCommand(program: Command, ctx: CommandContext) {
const l = ctx.l;
const SyncGroup = l.r('cli.cmd.root.sub.sync');
program.commandsGroup(SyncGroup);
program
.command('contacts')
.summary(l.r('cli.cmd.contacts.summary'))
.description(l.r('cli.cmd.contacts.description'))
.helpGroup(SyncGroup)
.addArgument(
new Argument(
'[action]',
l.r('cli.cmd.contacts.action.description'),
).choices(['pull', 'status', 'help']),
)
.addOption(
new Option(
'-a, --avatar <mode>',
l.r('cli.cmd.contacts.pull.option.avatar'),
)
.choices(['if-missing', 'update'])
.preset('if-missing'),
)
.option('-g, --groups', l.r('cli.cmd.contacts.pull.option.groups'))
.option('-l, --lids', l.r('cli.cmd.contacts.pull.option.lids'))
.option(
'--na, --no-attributes',
l.r('cli.cmd.contacts.pull.option.no-attributes'),
)
.option(
'-b, --batch <number>',
l.r('cli.cmd.contacts.pull.option.batch'),
NotNegativeNumber,
100,
)
.addOption(
ProgressOption(l.r('cli.cmd.contacts.pull.option.progress'), 100),
)
.addOption(new JobAttemptsOption(l, 6))
.addOption(new JobTimeoutOption(l, '10m'))
.addOption(
new Option(
'--dc, --delay-contact <duration>',
l.r('cli.cmd.contacts.pull.option.delay-contact'),
)
.argParser(ParseMS)
.default(ParseMS('0.1s'), '0.1s'),
)
.addOption(
new Option(
'--db, --delay-batch <duration>',
l.r('cli.cmd.contacts.pull.option.delay-batch'),
)
.argParser(ParseMS)
.default(ParseMS('1s'), '1s'),
)
.action(async (action, opts, cmd: Command) => {
if (!action) {
cmd.outputHelp();
return;
}
if (action === 'help') {
cmd.outputHelp();
return;
}
if (action === 'status') {
await ContactsPullStatus(ctx);
return;
}
const options: ContactsPullOptions = {
batch: opts.batch,
progress: opts.progress,
avatar: opts.avatar,
attributes: opts.attributes,
contacts: {
lids: opts.lids,
groups: opts.groups,
},
delay: {
contact: opts.delayContact,
batch: opts.delayBatch,
},
};
const jobOptions: JobsOptions & JobDataTimeout = {
attempts: opts.attempts,
timeout: {
job: opts.timeout,
},
};
await ContactsPullStart(ctx, options, jobOptions);
});
}
+142
View File
@@ -0,0 +1,142 @@
import { Argument, Command, Option } from 'commander';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import {
JobAttemptsOption,
JobTimeoutOption,
ParseMS,
NotNegativeNumber,
ProgressOption,
} from '@waha/apps/chatwoot/cli/utils/options';
import {
MessagesPullStart,
MessagesPullStatus,
} from '@waha/apps/chatwoot/cli/cmd.messages';
import { MessagesPullOptions } from '@waha/apps/chatwoot/consumers/task/messages.pull';
import { JobsOptions } from 'bullmq';
import { JobDataTimeout } from '@waha/apps/app_sdk/AppConsumer';
export function AddMessagesCommand(
program: Command,
ctx: CommandContext,
queue: boolean,
) {
const l = ctx.l;
const SyncGroup = l.r('cli.cmd.root.sub.sync');
program.commandsGroup(SyncGroup);
program
.command('messages')
.summary(l.r('cli.cmd.messages.summary'))
.description(l.r('cli.cmd.messages.description'))
.helpGroup(SyncGroup)
.addArgument(
new Argument(
'[action]',
l.r('cli.cmd.messages.action.description'),
).choices(['pull', 'status', 'help']),
)
.addArgument(
new Argument('[end]', l.r('cli.cmd.messages.pull.argument.end'))
.argParser(ParseMS)
.default(ParseMS('1d'), '1d'),
)
.addArgument(
new Argument('[start]', l.r('cli.cmd.messages.pull.argument.start'))
.argParser(ParseMS)
.default(ParseMS('0d'), '0d'),
)
.option(
'-c, --chat <chat>',
l.r('cli.cmd.messages.pull.option.chat'),
'all',
)
.option('-f, --force', l.r('cli.cmd.messages.pull.option.force'))
.option('--nd, --no-dm', l.r('cli.cmd.messages.pull.option.no-dm'))
.option('-g, --groups', l.r('cli.cmd.messages.pull.option.groups'))
.option('--ch, --channels', l.r('cli.cmd.messages.pull.option.channels'))
.option('-s, --status', l.r('cli.cmd.messages.pull.option.status'))
.option('--bc, --broadcast', l.r('cli.cmd.messages.pull.option.broadcast'))
.option('-m, --media', l.r('cli.cmd.messages.pull.option.media'))
.option('--pause', l.r('cli.cmd.messages.pull.option.pause'))
.option(
'--rc, --resolve-conversations',
l.r('cli.cmd.messages.pull.option.resolve-conversations'),
)
.option(
'-b, --batch <number>',
l.r('cli.cmd.messages.pull.option.batch'),
NotNegativeNumber,
100 as any,
)
.addOption(ProgressOption(l.r('cli.cmd.messages.pull.option.progress')))
.addOption(new JobAttemptsOption(l, 6))
.addOption(new JobTimeoutOption(l, '10m'))
.addOption(
new Option(
'--tm, --timeout-media <duration>',
l.r('cli.cmd.messages.pull.option.timeout-media'),
)
.argParser(ParseMS)
.default(ParseMS('30s'), '30s'),
)
.action(async (action, end, start, opts, cmd: Command) => {
if (!action) {
cmd.outputHelp();
return;
}
if (action === 'help') {
cmd.outputHelp();
return;
}
if (action === 'status') {
await MessagesPullStatus(ctx);
return;
}
if (end > start) {
// Swap
const tmp = end;
end = start;
start = tmp;
}
if (opts.pause && !queue) {
await ctx.conversation.incoming(
l.r('cli.cmd.messages.pull.error.pause-no-queue'),
);
return;
}
const options: MessagesPullOptions = {
chat: opts.chat,
progress: opts.progress,
period: {
end: end,
start: start,
},
media: opts.media,
force: opts.force,
pause: opts.pause,
timeout: {
media: opts.timeoutMedia,
},
batch: opts.batch,
ignore: {
dm: !opts.dm,
status: !opts.status,
groups: !opts.groups,
channels: !opts.channels,
broadcast: !opts.broadcast,
},
resolveConversations: opts.resolveConversations,
};
const jobOptions: JobsOptions & JobDataTimeout = {
attempts: opts.attempts,
timeout: {
job: opts.timeout,
},
};
await MessagesPullStart(ctx, options, jobOptions);
});
}
+66
View File
@@ -0,0 +1,66 @@
import { Argument, Command } from 'commander';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { CommandDisabled } from '@waha/apps/chatwoot/cli/cmd.disabled';
import {
QueueStart,
QueueStatus,
QueueStop,
} from '@waha/apps/chatwoot/cli/cmd.queue';
export function AddQueueCommand(
program: Command,
ctx: CommandContext,
enabled: boolean,
) {
const l = ctx.l;
const QueueGroup = l.r('cli.cmd.root.sub.queue');
program.commandsGroup(QueueGroup);
program
.command('queue', { hidden: !enabled })
.alias('q')
.summary(l.r('cli.cmd.queue.summary'))
.description(l.r('cli.cmd.queue.description'))
.helpGroup(QueueGroup)
.addArgument(
new Argument('[action]', l.r('cli.cmd.queue.action.description'))
.choices(['status', 'start', 'stop', 'help'])
.default('help'),
)
.addArgument(
new Argument('[name]', l.r('cli.cmd.queue.argument.name')).default(''),
)
.action(async function (this: Command, action: string, name: string) {
if (!action) {
this.outputHelp();
return;
}
if (action === 'help') {
this.outputHelp();
return;
}
if (!enabled) {
await CommandDisabled(ctx, 'queue');
return;
}
if (action === 'status') {
await QueueStatus(ctx, name);
return;
}
if (action === 'start') {
await QueueStart(ctx, name);
return;
}
if (action === 'stop') {
await QueueStop(ctx, name);
return;
}
this.outputHelp();
});
}
+38
View File
@@ -0,0 +1,38 @@
import { Command } from 'commander';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { CommandDisabled } from '@waha/apps/chatwoot/cli/cmd.disabled';
import { ServerReboot, ServerStatus } from '@waha/apps/chatwoot/cli/cmd.server';
export function AddServerCommand(
program: Command,
ctx: CommandContext,
enabled: boolean,
) {
const l = ctx.l;
const ServerGroup = l.r('cli.cmd.root.sub.server');
program.commandsGroup(ServerGroup);
const server = program
.command('server', { hidden: !enabled })
.description(l.r('cli.cmd.server.description'))
.helpGroup(ServerGroup);
if (!enabled) {
// Do not show help, show disabled ASAP
server.action(() => CommandDisabled(ctx, 'server'));
}
server
.command('status')
.description(l.r('cli.cmd.server.status.description'))
.action(() =>
enabled ? ServerStatus(ctx) : CommandDisabled(ctx, 'server status'),
);
server
.command('reboot')
.description(l.r('cli.cmd.server.reboot.description'))
.option('-f, --force', l.r('cli.cmd.server.reboot.option.force'), false)
.action((opts) =>
enabled
? ServerReboot(ctx, opts.force)
: CommandDisabled(ctx, 'server reboot'),
);
}
+60
View File
@@ -0,0 +1,60 @@
import { Command } from 'commander';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import {
SessionLogout,
SessionQR,
SessionRestart,
SessionScreenshot,
SessionStart,
SessionStatus,
SessionStop,
} from '@waha/apps/chatwoot/cli/cms.session';
export function AddSessionCommand(program: Command, ctx: CommandContext) {
const l = ctx.l;
const SessionGroup = l.r('cli.cmd.root.sub.session');
program.commandsGroup(SessionGroup);
program
.command('status')
.alias('1')
.description(l.r('cli.cmd.session.status.description'))
.helpGroup(SessionGroup)
.action(() => SessionStatus(ctx));
program
.command('restart')
.alias('2')
.description(l.r('cli.cmd.session.restart.description'))
.helpGroup(SessionGroup)
.action(() => SessionRestart(ctx));
program
.command('start')
.alias('3')
.description(l.r('cli.cmd.session.start.description'))
.helpGroup(SessionGroup)
.action(() => SessionStart(ctx));
program
.command('stop')
.alias('4')
.description(l.r('cli.cmd.session.stop.description'))
.helpGroup(SessionGroup)
.action(() => SessionStop(ctx));
program
.command('logout')
.alias('5')
.description(l.r('cli.cmd.session.logout.description'))
.helpGroup(SessionGroup)
.action(() => SessionLogout(ctx));
program
.command('qr')
.alias('6')
.description(l.r('cli.cmd.session.qr.description'))
.helpGroup(SessionGroup)
.action(() => SessionQR(ctx));
program
.command('screenshot')
.alias('7')
.description(l.r('cli.cmd.session.screenshot.description'))
.helpGroup(SessionGroup)
.action(() => SessionScreenshot(ctx));
}
+23
View File
@@ -0,0 +1,23 @@
import { WAHASelf } from '@waha/apps/app_sdk/waha/WAHASelf';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { Conversation } from '../client/Conversation';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { FlowProducer, Queue } from 'bullmq';
import { InboxData } from '@waha/apps/chatwoot/consumers/types';
import { QueueRegistry } from '@waha/apps/chatwoot/services/QueueRegistry';
export interface CommandContext {
data: InboxData;
logger: ILogger;
l: Locale;
waha: WAHASelf;
conversation: Conversation;
queues: {
registry: QueueRegistry;
contactsPull: Queue;
messagesPull: Queue;
};
flows: {
messagesPull: FlowProducer;
};
}
@@ -0,0 +1,26 @@
import { OutputConfiguration } from 'commander';
export class BufferedOutput implements OutputConfiguration {
private outBuffer: string[] = [];
private errBuffer: string[] = [];
writeOut = (str: string): void => {
this.outBuffer.push(str);
};
writeErr = (str: string): void => {
this.errBuffer.push(str);
};
outputError = (str: string, write: (s: string) => void) => {
this.errBuffer.push(str);
};
get out() {
return this.outBuffer.join('');
}
get err() {
return this.errBuffer.join('');
}
}
+132
View File
@@ -0,0 +1,132 @@
import { Command, Help, Option } from 'commander';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
export function fullCommandPath(sub: Command): string {
const parts: string[] = [];
let cmd: Command | null = sub;
// collect names up to (but not including) the root program
const name = cmd.name();
if (name) parts.push(name);
while (cmd && cmd.parent) {
cmd = cmd.parent!;
const name = cmd.name();
if (name && name != 'program') parts.push(name);
}
return parts.reverse().join(' ');
}
export function buildFormatHelp(l: Locale) {
/**
* Commands first, then Arguments, then Options
* @param cmd
* @param helper
*/
return function formatHelp(this: Help, cmd: Command, helper: Help) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth ?? 80;
const callFormatItem = (term: string, description: string) =>
helper.formatItem(term, termWidth, description, helper);
let output: string[] = [];
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) {
output = output.concat([
helper.boxWrap(
helper.styleCommandDescription(commandDescription),
helpWidth,
),
'',
]);
}
const usage = helper.commandUsage(cmd);
if (usage) {
output.push(
`${
helper.styleTitle(l.r('cli.help.usage.title')) + '\n'
} ${helper.styleUsage(helper.commandUsage(cmd))}`,
);
output.push('');
}
const commandGroups = this.groupItems(
cmd.commands as Command[],
helper.visibleCommands(cmd),
(sub) => sub.helpGroup() || l.r('cli.help.commands.defaultGroup'),
);
commandGroups.forEach((commands, group) => {
if (!commands.length) {
return;
}
const commandLines = commands.map((sub) => {
const commandTerm = helper.styleSubcommandTerm(
helper.subcommandTerm(sub),
);
const description = helper.subcommandDescription(sub);
const styledDescription = description
? helper.styleSubcommandDescription(description)
: '';
return `${commandTerm} ${styledDescription}`;
});
output.push(helper.styleTitle(group));
output.push(...commandLines);
output.push('');
});
const argumentList = helper
.visibleArguments(cmd)
.map((argument) =>
callFormatItem(
helper.styleArgumentTerm(helper.argumentTerm(argument)),
helper.styleArgumentDescription(helper.argumentDescription(argument)),
),
);
output = output.concat(
this.formatItemList(
l.r('cli.help.arguments.title'),
argumentList,
helper,
),
);
const optionGroups = this.groupItems(
cmd.options as Option[],
helper.visibleOptions(cmd),
(option) =>
option.helpGroupHeading ?? l.r('cli.help.options.defaultGroup'),
);
optionGroups.forEach((options, group) => {
const optionList = options.map((option) =>
callFormatItem(
helper.styleOptionTerm(helper.optionTerm(option)),
helper.styleOptionDescription(helper.optionDescription(option)),
),
);
output = output.concat(this.formatItemList(group, optionList, helper));
});
if (helper.showGlobalOptions) {
const globalOptionList = helper
.visibleGlobalOptions(cmd)
.map((option) =>
callFormatItem(
helper.styleOptionTerm(helper.optionTerm(option)),
helper.styleOptionDescription(helper.optionDescription(option)),
),
);
output = output.concat(
this.formatItemList(
l.r('cli.help.globalOptions.title'),
globalOptionList,
helper,
),
);
}
return output.join('\n');
};
}
+65
View File
@@ -0,0 +1,65 @@
import * as ms from 'ms';
import { Option } from 'commander';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
/**
* Parse human string into milliseconds
*/
export function ParseMS(value: string) {
const duration = ms(value);
if (duration == null) throw new Error(`Invalid duration: "${value}"`);
if (duration < 0) throw new Error(`Duration cannot be negative: "${value}"`);
return duration;
}
export function ParseSeconds(value: string): number {
const duration = ParseMS(value);
return Math.floor(duration / 1000);
}
export class JobAttemptsOption extends Option {
constructor(l: Locale, def: number) {
super('--at, --attempts <number>', l.r('cli.cmd.options.job.attempts'));
this.argParser(NotNegativeNumber);
this.default(def);
}
}
export class JobTimeoutOption extends Option {
constructor(l: Locale, def: string) {
super('-t, --timeout <duration>', l.r('cli.cmd.options.job.timeout'));
this.argParser(ParseMS);
this.default(ParseMS(def));
}
}
export function ProgressOption(
description: string,
def: number = 1000,
): Option {
return new Option('-p, --progress [number]', description)
.argParser(NotNegativeNumber)
.default(def);
}
export function NotNegativeNumber(value: string): number {
const n = parseInt(value, 10);
if (isNaN(n)) {
throw new Error(`Invalid number: "${value}"`);
}
if (n < 0) {
throw new Error(`Number must be positive or 0: "${value}"`);
}
return n;
}
export function PositiveNumber(value: string): number {
const n = parseInt(value, 10);
if (isNaN(n)) {
throw new Error(`Invalid number: "${value}"`);
}
if (n <= 0) {
throw new Error(`Number must be positive: "${value}"`);
}
return n;
}
@@ -3,10 +3,14 @@ import ChatwootClient, {
public_contact_create_update_payload,
} from '@figuro/chatwoot-sdk';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { ContactService } from '@waha/apps/chatwoot/client/ContactService';
import {
AvatarUpdateMode,
ContactService,
} from '@waha/apps/chatwoot/client/ContactService';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import {
ContactIds,
ConversationResult,
ConversationService,
} from '@waha/apps/chatwoot/client/ConversationService';
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
@@ -40,9 +44,10 @@ export class ContactConversationService {
this.cache = CacheForConfig(config);
}
private async upsertByContactInfo(
private async getByContactInfo(
contactInfo: ContactInfo,
): Promise<ContactIds> {
upsert: boolean,
): Promise<ContactIds | null> {
const chatId = contactInfo.ChatId();
// Check cache for chat id
@@ -50,65 +55,67 @@ export class ContactConversationService {
return this.cache.get(chatId);
}
//
// Find or create contact
//
let contact = await this.contactService.searchByAnyID(chatId);
if (!contact) {
const request = await contactInfo.PublicContactCreate();
contact = await this.contactService.create(chatId, request);
}
let [cwContact, created] =
await this.contactService.findOrCreateContact(contactInfo);
// Update custom attributes - always
const attributes = await contactInfo.Attributes();
this.logger.info(
`Updating if required contact custom attributes for chat.id: ${chatId}, contact.id: ${contact.data.id}`,
this.logger.debug(
`Updating if required contact custom attributes for chat.id: ${chatId}, contact.id: ${cwContact.data.id}`,
);
await this.contactService.upsertCustomAttributes(contact.data, attributes);
// Update Avatar if nothing, but keep the original one if any
if (!contact.data.thumbnail) {
const avatarUrl = await contactInfo.AvatarUrl().catch((err) => {
this.logger.warn(
`Error getting avatar for chat.id from WhatsApp: ${chatId}`,
);
this.logger.warn(err);
return null;
});
if (avatarUrl) {
this.contactService.updateAvatarUrlSafe(contact.data.id, avatarUrl);
}
}
this.logger.info(
`Using contact for chat.id: ${chatId}, contact.id: ${contact.data.id}, contact.sourceId: ${contact.sourceId}`,
const attributes = await contactInfo.Attributes();
await this.contactService.upsertCustomAttributes(
cwContact.data,
attributes,
);
await this.contactService.updateAvatar(
cwContact,
contactInfo,
AvatarUpdateMode.IF_MISSING,
);
this.logger.debug(
`Using contact for chat.id: ${chatId}, contact.id: ${cwContact.data.id}, contact.sourceId: ${cwContact.sourceId}`,
);
//
// Get or create a conversation for this inbox
//
const conversation = await this.conversationService.upsert({
id: contact.data.id,
sourceId: contact.sourceId,
});
this.logger.info(
`Using conversation for chat.id: ${chatId}, conversation.id: ${conversation.id}, contact.id: ${contact.sourceId}`,
const contactIds = {
id: cwContact.data.id,
sourceId: cwContact.sourceId,
};
let conversation: ConversationResult;
if (upsert) {
conversation = await this.conversationService.upsert(contactIds);
} else {
conversation = await this.conversationService.find(contactIds);
if (!conversation) {
// Nothing found, do not save it in cache
return null;
}
}
this.logger.debug(
`Using conversation for chat.id: ${chatId}, conversation.id: ${conversation.id}, contact.id: ${cwContact.sourceId}`,
);
// Save to cache
const ids = {
id: conversation.id,
sourceId: contact.sourceId,
sourceId: cwContact.sourceId,
};
this.cache.set(chatId, ids);
return ids;
}
public async ConversationByContact(
private async conversationByContact(
contactInfo: ContactInfo,
): Promise<Conversation> {
upsert: boolean,
): Promise<Conversation | null> {
const chatId = contactInfo.ChatId();
const ids = await this.upsertByContactInfo(contactInfo);
const ids = await this.getByContactInfo(contactInfo, upsert);
if (!ids) {
return null;
}
const conversation = new Conversation(
this.accountAPI,
this.config.accountId,
@@ -128,6 +135,26 @@ export class ContactConversationService {
return conversation;
}
/**
* Find or create if not exists conversation for the contact
*/
public async ConversationByContact(contactInfo: ContactInfo) {
const conversation = await this.conversationByContact(contactInfo, true);
if (!conversation) {
// Shouldn't happen, but for type safety
throw new Error('Conversation could not be created or found');
}
return conversation;
}
/**
* Find suitable conversation for the contact (based on filters)
* If nothing found - it doesn't create a new one
*/
public async FindConversationByContact(contactInfo: ContactInfo) {
return this.conversationByContact(contactInfo, false);
}
public ConversationById(conversationId: number): Conversation {
return new Conversation(
this.accountAPI,
@@ -145,7 +172,7 @@ export class ContactConversationService {
}
public ResetCache(chatIds: Array<string>) {
this.logger.info(`Resetting cache chat ids: ${chatIds.join(', ')}`);
this.logger.debug(`Resetting cache chat ids: ${chatIds.join(', ')}`);
for (const chatId of chatIds) {
this.cache.delete(chatId);
}
@@ -158,7 +185,7 @@ export class ContactConversationService {
}
const current = this.cache.get(chatId);
if (current.id !== contactId) {
this.logger.info(
this.logger.debug(
`Resetting cache for chat id: ${chatId}, value changed from ${current} to ${contactId}`,
);
this.cache.delete(chatId);
@@ -0,0 +1,23 @@
import { sanitizeName } from '@waha/apps/chatwoot/client/ContactService';
describe('sanitizeName', () => {
it('should return the same name', () => {
const name = 'John Doe';
expect(sanitizeName(name)).toBe(name);
});
it('should return 254 symbols max', () => {
const name = 'a'.repeat(300);
expect(sanitizeName(name)).toBe('a'.repeat(255));
});
it('should remove bidi characters and trim the name', () => {
const name =
'‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪‏‪+123123‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏‬‏';
expect(sanitizeName(name)).toBe('+123123');
});
it('should not remove bidi characters if within limit', () => {
const name = 'محمد +123';
expect(sanitizeName(name)).toBe(name);
});
});
+65 -2
View File
@@ -14,12 +14,32 @@ import * as lodash from 'lodash';
import { AttributeKey } from '../const';
import { E164Parser } from '@waha/core/utils/PhoneJidNormalizer';
import { ContactInfo } from '@waha/apps/chatwoot/client/ContactConversationService';
export interface ContactResponse {
data: generic_id & contact;
sourceId: string;
}
export enum AvatarUpdateMode {
IF_MISSING,
ALWAYS,
}
export function sanitizeName(name: string) {
// 255 chars max
const limit = 255;
if (!name) {
return name;
}
if (name.length < limit) {
return name;
}
// remove only bidi controls
const clean = name.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, '');
return clean.slice(0, 255).trim();
}
export class ContactService {
constructor(
private config: ChatWootAPIConfig,
@@ -28,6 +48,20 @@ export class ContactService {
private logger: ILogger,
) {}
async findOrCreateContact(
contactInfo: ContactInfo,
): Promise<[ContactResponse, boolean]> {
const chatId = contactInfo.ChatId();
let contact = await this.searchByAnyID(chatId);
if (contact) {
return [contact, false];
}
const request = await contactInfo.PublicContactCreate();
contact = await this.create(chatId, request);
return [contact, true];
}
async searchByAnyID(chatId: string): Promise<ContactResponse | null> {
const payload: any[] = [
{
@@ -119,6 +153,7 @@ export class ContactService {
chatId: string,
payload: public_contact_create_update_payload,
): Promise<ContactResponse> {
payload.name = sanitizeName(payload.name);
const contact = await this.inboxAPI.contacts.create({
inboxIdentifier: this.config.inboxIdentifier,
data: payload,
@@ -136,8 +171,32 @@ export class ContactService {
};
}
public updateAvatarUrlSafe(contactId, avatarUrl: string) {
this.accountAPI.contacts
public async updateAvatar(
contact: ContactResponse,
contactInfo: ContactInfo,
mode: AvatarUpdateMode,
): Promise<boolean> {
// Update Avatar if nothing, but keep the original one if any
if (contact.data.thumbnail && mode == AvatarUpdateMode.IF_MISSING) {
return false;
}
const chatId = contactInfo.ChatId();
const avatarUrl = await contactInfo.AvatarUrl().catch((err) => {
this.logger.warn(
`Error getting avatar for chat.id from WhatsApp: ${chatId}`,
);
this.logger.warn(err);
return null;
});
if (!avatarUrl) {
return false;
}
const success = await this.updateAvatarUrlSafe(contact.data.id, avatarUrl);
return success;
}
public updateAvatarUrlSafe(contactId, avatarUrl: string): Promise<boolean> {
return this.accountAPI.contacts
.update({
accountId: this.config.accountId,
id: contactId,
@@ -145,11 +204,15 @@ export class ContactService {
avatar_url: avatarUrl,
},
})
.then(() => {
return true;
})
.catch((e) => {
this.logger.warn(
`Error updating avatar_url for contact.id: ${contactId}`,
);
this.logger.warn(e);
return true;
});
}
}
+20 -2
View File
@@ -4,6 +4,7 @@ import { MessageType } from '@waha/apps/chatwoot/client/types';
export class Conversation {
public onError: (e: any) => void;
public overrideIncoming: any = null;
constructor(
private accountAPI: ChatwootClient,
@@ -13,6 +14,9 @@ export class Conversation {
) {}
public async send(data: conversation_message_create) {
if (data.message_type === MessageType.INCOMING && this.overrideIncoming) {
data = { ...data, ...this.overrideIncoming };
}
try {
const message = await this.accountAPI.messages.create({
accountId: this.accountId,
@@ -29,10 +33,13 @@ export class Conversation {
}
public async incoming(text: string) {
const data: conversation_message_create = {
let data: conversation_message_create = {
content: text,
message_type: MessageType.INCOMING as any,
};
if (this.overrideIncoming) {
data = { ...data, ...this.overrideIncoming };
}
return this.send(data);
}
@@ -44,11 +51,22 @@ export class Conversation {
return this.send(data);
}
public async outgoing(text: string) {
public async note(text: string) {
const data: conversation_message_create = {
content: text,
private: true,
message_type: MessageType.OUTGOING as any,
};
return this.send(data);
}
/**
* Force a note message (private outgoing) for all communications
*/
public forceNote() {
this.overrideIncoming = {
private: true,
message_type: MessageType.OUTGOING as any,
};
}
}
@@ -6,7 +6,6 @@ import {
} from '@waha/apps/chatwoot/client/interfaces';
import type { conversation } from '@figuro/chatwoot-sdk/dist/models/conversation';
import { ConversationSelector } from '@waha/apps/chatwoot/services/ConversationSelector';
import axios from 'axios';
import { ChatWootInboxNewAPI } from '@waha/apps/chatwoot/client/ChatWootInboxNewAPI';
export type ConversationResult = Pick<conversation, 'id' | 'account_id'>;
@@ -25,14 +24,20 @@ export class ConversationService {
private logger: ILogger,
) {}
private async find(contact: ContactIds): Promise<ConversationResult | null> {
public async find(contact: ContactIds): Promise<ConversationResult | null> {
const result: { payload: contact_conversations } =
(await this.accountAPI.contacts.listConversations({
accountId: this.config.accountId,
id: contact.id,
})) as any;
const conversations = result.payload;
return this.selector.select(conversations);
const conversation = this.selector.select(conversations);
if (conversation) {
this.logger.debug(
`Found existing conversation.id: ${conversation.id} for contact.id: ${contact.id}, contact.sourceId: ${contact.sourceId}`,
);
}
return conversation;
}
private async create(contact: ContactIds): Promise<ConversationResult> {
@@ -40,7 +45,7 @@ export class ConversationService {
inboxIdentifier: this.config.inboxIdentifier,
contactIdentifier: contact.sourceId,
});
this.logger.info(
this.logger.debug(
`Created conversation.id: ${conversation.id} for contact.id: ${contact.id}, contact.sourceId: ${contact.sourceId}`,
);
return conversation;
@@ -51,9 +56,6 @@ export class ConversationService {
if (!conversation) {
conversation = await this.create(contact);
}
this.logger.info(
`Using conversation.id: ${conversation.id} for contact.id: ${contact.id}, contact.sourceId: ${contact.sourceId}`,
);
return conversation;
}
@@ -75,4 +77,14 @@ export class ConversationService {
throw error;
}
}
async resolve(conversationId: number): Promise<any> {
return this.accountAPI.conversations.toggleStatus({
accountId: this.config.accountId,
conversationId: conversationId,
data: {
status: 'resolved',
},
});
}
}
+9 -2
View File
@@ -1,4 +1,5 @@
import { AttributeKey } from '@waha/apps/chatwoot/const';
import { AttributeKey, INBOX_CONTACT_CHAT_ID } from '@waha/apps/chatwoot/const';
import * as lodash from 'lodash';
import { WhatsAppMessage } from '@waha/apps/chatwoot/storage';
import { buildMessageId } from '@waha/core/engines/noweb/session.noweb.core';
import { isLidUser } from '@waha/core/utils/jids';
@@ -22,7 +23,7 @@ export function GetAllChatIDs(contact: any): Array<string> {
attrs[AttributeKey.WA_JID],
attrs[AttributeKey.WA_LID],
];
return ids.filter(Boolean);
return lodash.uniq(ids.filter(Boolean));
}
export function FindChatID(contact: any): string | null {
@@ -55,3 +56,9 @@ export function ContactAttr(chatId: string): AttributeKey {
return AttributeKey.WA_JID;
}
}
export function IsCommandsChat(body): boolean {
const sender = body?.conversation?.meta?.sender;
const chatId = FindChatID(sender);
return chatId === INBOX_CONTACT_CHAT_ID;
}
+10
View File
@@ -5,6 +5,12 @@ export enum QueueName {
SCHEDULED_MESSAGE_CLEANUP = 'chatwoot.scheduled | message.cleanup',
SCHEDULED_CHECK_VERSION = 'chatwoot.scheduled | check.version',
//
// Task
//
TASK_CONTACTS_PULL = 'chatwoot.task | contacts.pull',
TASK_MESSAGES_PULL = 'chatwoot.task | messages.pull',
//
// WAHA Events
//
@@ -27,3 +33,7 @@ export enum QueueName {
INBOX_MESSAGE_DELETED = 'chatwoot.inbox | message_deleted',
INBOX_COMMANDS = 'chatwoot.inbox | commands',
}
export enum FlowProducerName {
MESSAGES_PULL_FLOW = 'messages.pull.flow',
}
+10 -7
View File
@@ -10,7 +10,7 @@ import {
ChatIDNotFoundForContactError,
PhoneNumberNotFoundInWhatsAppError,
} from '@waha/apps/chatwoot/errors';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
@@ -18,6 +18,7 @@ import { PinoLogger } from 'nestjs-pino';
import { AppRepository } from '../../storage';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
/**
* Base class for ChatWoot inbox consumers
@@ -90,6 +91,12 @@ export abstract class ChatWootInboxMessageConsumer extends AppConsumer {
}
}
protected conversationForReport(container, body): Conversation {
return container
.ContactConversationService()
.ConversationById(body.conversation.id);
}
/**
* Report an error for a message
*/
@@ -98,9 +105,7 @@ export abstract class ChatWootInboxMessageConsumer extends AppConsumer {
const header: string = this.ErrorHeaderKey()
? container.Locale().key(this.ErrorHeaderKey()).render()
: err.message || `${err}`;
const conversation = container
.ContactConversationService()
.ConversationById(body.conversation.id);
const conversation = this.conversationForReport(container, body);
const reporter = container.ChatWootErrorReporter(job);
await reporter.ReportError(
conversation,
@@ -118,9 +123,7 @@ export abstract class ChatWootInboxMessageConsumer extends AppConsumer {
}
const container = await this.DIContainer(job, job.data.app);
const conversation = container
.ContactConversationService()
.ConversationById(body.conversation.id);
const conversation = this.conversationForReport(container, body);
const reporter = container.ChatWootErrorReporter(job);
await reporter.ReportSucceeded(conversation, body.message_type, body.id);
}
+49 -196
View File
@@ -1,21 +1,20 @@
import { Processor } from '@nestjs/bullmq';
import { InjectFlowProducer, InjectQueue, Processor } from '@nestjs/bullmq';
import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { ContactConversationService } from '@waha/apps/chatwoot/client/ContactConversationService';
import { AttachmentFromBuffer } from '@waha/apps/chatwoot/client/messages';
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 {
FlowProducerName,
QueueName,
} from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
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 { FlowProducer, Job, Queue } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { CommandPrefix, runText } from '@waha/apps/chatwoot/cli';
import { CommandContext } from '@waha/apps/chatwoot/cli/types';
import { IsCommandsChat } from '@waha/apps/chatwoot/client/ids';
import { QueueRegistry } from '@waha/apps/chatwoot/services/QueueRegistry';
@Processor(QueueName.INBOX_COMMANDS, { concurrency: JOB_CONCURRENCY })
export class ChatWootInboxCommandsConsumer extends ChatWootInboxMessageConsumer {
@@ -23,6 +22,9 @@ export class ChatWootInboxCommandsConsumer extends ChatWootInboxMessageConsumer
protected readonly manager: SessionManager,
log: PinoLogger,
rmutex: RMutexService,
private readonly queueRegistry: QueueRegistry,
@InjectFlowProducer(FlowProducerName.MESSAGES_PULL_FLOW)
private readonly messagesPullFlow: FlowProducer,
) {
super(manager, log, rmutex, 'ChatWootInboxCommandsConsumer');
}
@@ -31,196 +33,47 @@ export class ChatWootInboxCommandsConsumer extends ChatWootInboxMessageConsumer
return null;
}
protected conversationForReport(container, body) {
const conversation = super.conversationForReport(container, body);
if (!IsCommandsChat(body)) {
conversation.forceNote();
}
return conversation;
}
protected async Process(container: DIContainer, body, job: Job) {
const handler = new SessionCommandHandler(
container.Logger(),
container.ContactConversationService(),
job.data.session,
container.Locale(),
container.WAHASelf(),
container.ChatWootConfig().commands,
);
return await handler.handle(body);
}
}
const CommandServerPrefix = 'server ';
enum Command {
STATUS = 'status',
RESTART = 'restart',
START = 'start',
STOP = 'stop',
LOGOUT = 'logout',
QR = 'qr',
SCREENSHOT = 'screenshot',
HELP = 'help',
SERVER_STATUS = 'server status',
SERVER_REBOOT = 'server reboot',
SERVER_REBOOT_FORCE = 'server reboot force',
}
const CommandMap: Record<string, Command> = {
// Status
[Command.STATUS]: Command.STATUS,
['1']: Command.STATUS,
// Restart
[Command.RESTART]: Command.RESTART,
['2']: Command.RESTART,
// Start
[Command.START]: Command.START,
['3']: Command.START,
// Stop
[Command.STOP]: Command.STOP,
['4']: Command.STOP,
// Logout
[Command.LOGOUT]: Command.LOGOUT,
['5']: Command.LOGOUT,
// QR
[Command.QR]: Command.QR,
['6']: Command.QR,
// Screenshot
[Command.SCREENSHOT]: Command.SCREENSHOT,
['7']: Command.SCREENSHOT,
// Help
[Command.HELP]: Command.HELP,
['8']: Command.HELP,
// Server commands
[Command.SERVER_STATUS]: Command.SERVER_STATUS,
[Command.SERVER_REBOOT]: Command.SERVER_REBOOT,
[Command.SERVER_REBOOT_FORCE]: Command.SERVER_REBOOT_FORCE,
};
class CommandIsNotImplementedError extends Error {
constructor(private command: string) {
super(`Command is not implemented`);
}
}
class SessionCommandHandler {
constructor(
private logger: ILogger,
private repo: ContactConversationService,
private session: string,
private l: Locale,
private waha: WAHASelf,
private commands: ChatWootCommandsConfig,
) {}
async handle(message: any) {
const cmd = message.content?.toLowerCase();
const command = this.findCommand(cmd) || Command.HELP;
let cmd = body.content;
cmd = cmd.startsWith(CommandPrefix) ? cmd.slice(CommandPrefix.length) : cmd;
this.logger.info(
`Executing command ${command} for session ${this.session}`,
`Executing command '${cmd}' for session ${job.data.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;
const repo = container.ContactConversationService();
let conversation = repo.ConversationById(body.conversation.id);
if (!IsCommandsChat(body)) {
conversation.forceNote();
}
switch (command) {
case Command.RESTART:
await this.waha.restart(this.session);
break;
case Command.START:
await this.waha.restart(this.session);
break;
case Command.LOGOUT:
this.logger.info(`Logging out session ${this.session}`);
await this.waha.logout(this.session);
const text = this.l.key(TKey.APP_LOGOUT_SUCCESS).render();
await conversation.incoming(text);
break;
case Command.STOP:
this.logger.info(`Stopping session ${this.session}`);
await this.waha.stop(this.session);
break;
case Command.STATUS: {
this.logger.info(`Getting status for session ${this.session}`);
const session = await this.waha.get(this.session);
const emoji = SessionStatusEmoji(session.status);
const text = this.l.key(TKey.APP_SESSION_CURRENT_STATUS).render({
emoji: emoji,
session: session.name,
status: session.status,
name: session.me?.pushName || 'Unknown',
id: session.me?.id || 'No phone number',
});
await conversation.incoming(text);
break;
}
case Command.SERVER_STATUS: {
this.logger.info('Getting server version and status');
const version = await this.waha.serverVersion();
const status = await this.waha.serverStatus();
const text = this.l.key(TKey.APP_SERVER_VERSION_AND_STATUS).render({
version: JSON.stringify(version, null, 2),
status: JSON.stringify(status, null, 2),
});
await conversation.incoming(text);
break;
}
case Command.SERVER_REBOOT: {
this.logger.info('Rebooting server (graceful)');
const text = this.l.key(TKey.APP_SERVER_REBOOT).render();
await conversation.incoming(text);
await this.waha.serverReboot(false);
break;
}
case Command.SERVER_REBOOT_FORCE: {
this.logger.info('Rebooting server (force)');
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 content = await this.waha.qr(this.session);
const message = AttachmentFromBuffer(content, 'qr.jpg');
message.message_type = MessageType.INCOMING;
await conversation.send(message);
break;
}
case Command.SCREENSHOT: {
this.logger.info(`Getting screenshot for session ${this.session}`);
const content = await this.waha.screenshot(this.session);
const message = AttachmentFromBuffer(content, 'screenshot.jpg');
message.message_type = MessageType.INCOMING;
await conversation.send(message);
break;
}
case Command.HELP: {
const text = this.l
.key(TKey.APP_COMMANDS_LIST)
.render({ commands: this.commands });
await conversation.incoming(text);
break;
}
default:
throw new CommandIsNotImplementedError(cmd);
const ctx: CommandContext = {
data: job.data,
logger: this.logger,
l: container.Locale(),
waha: container.WAHASelf(),
conversation: conversation,
queues: {
registry: this.queueRegistry,
contactsPull: this.queueRegistry.queue(QueueName.TASK_CONTACTS_PULL),
messagesPull: this.queueRegistry.queue(QueueName.TASK_MESSAGES_PULL),
},
flows: {
messagesPull: this.messagesPullFlow,
},
};
const commands = container.ChatWootConfig().commands;
const output = await runText(commands, ctx, cmd);
if (output.out) {
await conversation.incoming(output.out);
}
}
/**
* 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;
if (output.err) {
await conversation.incoming(output.err);
}
return null;
}
findCommand(text: string): Command {
// Remove empty from end
text = text.trim();
// Remove empty from start
text = text.trimStart();
// Remove empty from end
text = text.trimEnd();
return CommandMap[text] || null;
}
}
@@ -8,8 +8,8 @@ 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 { EngineHelper } from '@waha/apps/chatwoot/session';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import {
ChatwootMessage,
MessageMapping,
@@ -8,7 +8,7 @@ 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 { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { MessageMappingService } from '@waha/apps/chatwoot/storage';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -56,6 +56,13 @@ class MessageDeletedHandler {
message_id: body.id,
});
if (!messages || messages.length == 0) {
if (body.private) {
// Private notes can have no whatsapp messages
this.logger.info(
`No WhatsApp message to delete for private note '${body.id}'`,
);
return;
}
throw Error(
`No WhatsApp message found for Chatwoot message '${body.id}'`,
);
@@ -9,7 +9,7 @@ import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { WAHASessionAPI } from '../../session/WAHASelf';
import { WAHASessionAPI } from '../../../app_sdk/waha/WAHASelf';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.INBOX_MESSAGE_UPDATED, { concurrency: JOB_CONCURRENCY })
@@ -4,7 +4,6 @@ import { HasBeenRetried } from '@waha/apps/app_sdk/JobUtils';
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 { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
@@ -19,7 +19,7 @@ import { TKey } from '@waha/apps/chatwoot/i18n/templates';
concurrency: JOB_CONCURRENCY,
})
export class MessageCleanupConsumer extends ChatWootScheduledConsumer {
REMOVE_AFTER_DAYS = 14;
REMOVE_AFTER_DAYS = 365;
constructor(manager: SessionManager, log: PinoLogger, rmutex: RMutexService) {
super(manager, log, rmutex, MessageCleanupConsumer.name);
+116
View File
@@ -0,0 +1,116 @@
import { AppConsumer } from '@waha/apps/app_sdk/AppConsumer';
import { JobLoggerWrapper } from '@waha/apps/app_sdk/JobLoggerWrapper';
import { HasBeenRetried } from '@waha/apps/app_sdk/JobUtils';
import { MessageType } from '@waha/apps/chatwoot/client/types';
import { ScheduledData } from '@waha/apps/chatwoot/consumers/types';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
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';
import { SignalRace } from '@waha/utils/abortable';
import { IsCommandsChat } from '@waha/apps/chatwoot/client/ids';
/**
* Base class for ChatWoot background task consumers
*/
export abstract class ChatWootTaskConsumer extends AppConsumer {
protected appRepository: AppRepository;
constructor(
protected readonly manager: SessionManager,
log: PinoLogger,
rmutex: RMutexService,
protected readonly consumerName: string,
) {
super('ChatWoot', consumerName, log, rmutex);
}
/**
* Gets the DIContainer for the specified app.
*/
protected async DIContainer(job: Job, appId: string): Promise<DIContainer> {
const knex = this.manager.store.getWAHADatabase();
this.appRepository = new AppRepository(knex);
const logger = new JobLoggerWrapper(job, this.logger);
const app = await this.appRepository.getById(appId);
return new DIContainer(app.pk, app.config, logger, knex);
}
protected abstract ErrorHeaderKey(): TKey | null;
/**
* Get the function that processes the scheduled job
* This method must be implemented by subclasses
*/
protected abstract Process(
container: DIContainer,
job: Job,
signal: AbortSignal,
): Promise<any>;
/**
* Process the job
* This method is called by the queue processor
*/
async processJob(job: Job<ScheduledData, any, string>): Promise<any> {
const signal = this.signal(job);
return SignalRace(this.ProcessAndReportStatus(job, signal), signal);
}
private async ProcessAndReportStatus(job: Job, signal: AbortSignal) {
try {
const container = await this.DIContainer(job, job.data.app);
const result = await this.Process(container, job, signal);
await this.ReportErrorRecovered(job);
return result;
} catch (err) {
this.ReportErrorForJob(job, err).catch((exc) =>
this.logger.error(`Failed to report error for job ${job.id}: ${exc}`),
);
throw err;
}
}
protected conversationForReport(container, body) {
const conversation = container
.ContactConversationService()
.ConversationById(body.conversation.id);
if (!IsCommandsChat(body)) {
conversation.forceNote();
}
return conversation;
}
/**
* Report an error for a scheduled job
*/
protected async ReportErrorForJob(job: Job, err: any) {
const container = await this.DIContainer(job, job.data.app);
let header = this.ErrorHeaderKey()
? container.Locale().key(this.ErrorHeaderKey()).render()
: err.message || `${err}`;
header = `${job.queueName}: ${header}`;
const conversation = this.conversationForReport(container, job.data.body);
const reporter = container.ChatWootErrorReporter(job);
await reporter.ReportError(conversation, header, MessageType.INCOMING, err);
throw err;
}
protected async ReportErrorRecovered(job: Job) {
if (!HasBeenRetried(job)) {
return;
}
const container = await this.DIContainer(job, job.data.app);
const conversation = this.conversationForReport(container, job.data.body);
const reporter = container.ChatWootErrorReporter(job);
await reporter.ReportSucceeded(conversation, MessageType.INCOMING);
}
}
@@ -0,0 +1,306 @@
import { Processor } from '@nestjs/bullmq';
import * as lodash from 'lodash';
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 { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job, JobState } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { ChatWootTaskConsumer } from '@waha/apps/chatwoot/consumers/task/base';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { WhatsAppContactInfo } from '@waha/apps/chatwoot/contacts/WhatsAppContactInfo';
import { ContactSortField } from '@waha/structures/contacts.dto';
import { SortOrder } from '@waha/structures/pagination.dto';
import {
AvatarUpdateMode,
ContactService,
} from '@waha/apps/chatwoot/client/ContactService';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
import { sleep } from '@waha/utils/promiseTimeout';
import { isJidGroup, isLidUser, isPnUser } from '@waha/core/utils/jids';
import {
ArrayPaginator,
PaginatorParams,
} from '@waha/apps/app_sdk/waha/Paginator';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
export interface ContactsPullOptions {
batch: number;
progress: number | null;
avatar: null | 'if-missing' | 'update';
attributes: boolean;
contacts: {
groups: boolean;
lids: boolean;
};
delay: {
contact: number;
batch: number;
};
}
/**
* Task consumer that pulls WhatsApp contacts into ChatWoot
* Runs in batches to keep the job resumable and observable in ChatWoot
*/
@Processor(QueueName.TASK_CONTACTS_PULL, { concurrency: JOB_CONCURRENCY })
export class TaskContactsPullConsumer extends ChatWootTaskConsumer {
constructor(manager: SessionManager, log: PinoLogger, rmutex: RMutexService) {
super(manager, log, rmutex, TaskContactsPullConsumer.name);
}
protected ErrorHeaderKey(): TKey {
return null;
}
/**
* Process the contact pull job
* Streams batches of contacts from WAHA into ChatWoot storage
*/
protected async Process(
container: DIContainer,
job: Job,
signal?: AbortSignal,
): Promise<any> {
const options: ContactsPullOptions = job.data.options;
const locale = container.Locale();
const waha = container.WAHASelf();
const session = new WAHASessionAPI(job.data.session, waha);
const conversation = this.conversationForReport(container, job.data.body);
const handler = new ContactsPullHandler(
signal,
container.Logger(),
conversation,
session,
locale,
container.ContactService(),
);
return await handler.handle(options, job);
}
}
interface Progress {
created: number;
updated: number;
skipped: number;
errors: number;
avatar: {
updated: number;
};
}
function total(progress: Progress) {
return (
progress.created + progress.updated + progress.skipped + progress.errors
);
}
const NullProgress: Progress = {
created: 0,
updated: 0,
skipped: 0,
errors: 0,
avatar: {
updated: 0,
},
};
class ContactsPullHandler {
private activity: TaskActivity;
constructor(
private signal: AbortSignal,
private logger: ILogger,
private conversation: Conversation,
private session: WAHASessionAPI,
private l: Locale,
private contactService: ContactService,
) {
this.activity = new TaskActivity(this.l, this.conversation);
}
async handle(options: ContactsPullOptions, job) {
const batch = options.batch;
let progress = lodash.merge({}, NullProgress, job.progress);
this.logger.debug(
`Pulling contacts for session ${job.data.session} with batch size ${batch}...`,
);
if (lodash.isEqual(progress, NullProgress)) {
await this.activity.started(progress);
}
const query = {
sortBy: ContactSortField.ID,
sortOrder: SortOrder.ASC,
limit: batch,
offset: 0,
};
const params: PaginatorParams = {
processed: total(progress),
};
const paginator = new ArrayPaginator<any>(params);
const contacts = paginator.iterate((processed: number) => {
query.offset = processed;
return this.session.getContacts(query, {
signal: this.signal,
});
});
for await (const contact of contacts) {
this.signal.throwIfAborted();
//
// Show progress
//
const thetotal = total(progress);
if (options.progress && thetotal) {
if (thetotal % options.progress == 0) {
await this.activity.progress(progress);
}
}
// Delay on batch
if (thetotal && options.delay.batch) {
if (thetotal % batch == 0) {
await sleep(options.delay.batch);
}
}
// Process
try {
if (!EngineHelper.ContactIsMy(contact)) {
progress.skipped += 1;
await job.updateProgress(progress);
continue;
}
const chatId = contact.id;
if (isJidGroup(chatId)) {
if (!options.contacts.groups) {
this.logger.debug(`Skipping group contact ${chatId}...`);
progress.skipped += 1;
await job.updateProgress(progress);
continue;
}
} else if (isLidUser(chatId)) {
if (!options.contacts.lids) {
this.logger.debug(`Skipping LID contact ${chatId}...`);
progress.skipped += 1;
await job.updateProgress(progress);
continue;
}
} else if (!isPnUser(chatId)) {
this.logger.info(`Skipping non-phone-number contact ${chatId}...`);
progress.skipped += 1;
await job.updateProgress(progress);
continue;
}
this.logger.debug(`Pulling ${chatId}...`);
const result = await this.pullOneContact(options, chatId);
progress.created += result.created;
progress.updated += result.updated;
progress.avatar.updated += result.avatar.updated;
this.logger.info(
`Contact ${chatId}: created=${result.created}, updated=${result.updated}, avatar.updated=${result.avatar.updated}`,
);
} catch (e) {
this.logger.error(`Error pulling contact ${contact.id}: ${e}`);
progress.errors += 1;
}
// update progress before signal fails
await job.updateProgress(progress);
await sleep(options.delay.contact);
}
await this.activity.completed(progress);
}
private async pullOneContact(options: ContactsPullOptions, chatId: string) {
// Contact
const contactInfo = WhatsAppContactInfo(this.session, chatId, this.l);
let [cwContact, created] =
await this.contactService.findOrCreateContact(contactInfo);
// Attributes
if (options.attributes) {
const attributes = await contactInfo.Attributes();
await this.contactService.upsertCustomAttributes(
cwContact.data,
attributes,
);
}
// Avatar
let avatarUpdated = false;
switch (options.avatar) {
case 'if-missing':
avatarUpdated = await this.contactService.updateAvatar(
cwContact,
contactInfo,
AvatarUpdateMode.IF_MISSING,
);
break;
case 'update':
avatarUpdated = await this.contactService.updateAvatar(
cwContact,
contactInfo,
AvatarUpdateMode.ALWAYS,
);
break;
}
return {
created: created ? 1 : 0,
updated: created ? 0 : 1,
avatar: {
updated: avatarUpdated ? 1 : 0,
},
};
}
}
class TaskActivity {
constructor(
private l: Locale,
private conversation: Conversation,
) {}
public async started(progress: Progress) {
await this.conversation.activity(
this.l.r('task.contacts.started', { progress: progress }),
);
}
public async progress(progress: Progress) {
await this.conversation.activity(
this.l.r('task.contacts.progress', { progress: progress }),
);
}
/**
* Final report
*/
public async completed(progress: Progress) {
await this.conversation.activity(
this.l.r('task.contacts.completed', { progress: progress }),
);
}
}
export function ContactsPullStatusMessage(
l: Locale,
job: Job,
state: JobState | 'unknown',
) {
const details = JobLink(job);
const progress = lodash.merge({}, NullProgress, job.progress);
const payload = {
error: state === 'failed' || state === 'unknown' || progress.errors > 0,
state: lodash.capitalize(state ?? 'unknown'),
progress: progress,
details: details,
};
return l.r('task.contacts.status', payload);
}
@@ -0,0 +1,523 @@
import { Processor } from '@nestjs/bullmq';
import * as ms from 'ms';
import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { ChatWootTaskConsumer } from '@waha/apps/chatwoot/consumers/task/base';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job, JobState } from 'bullmq';
import * as lodash from 'lodash';
import { PinoLogger } from 'nestjs-pino';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import {
GetChatMessagesFilter,
GetChatMessagesQuery,
MessageSortField,
} from '@waha/structures/chats.dto';
import { WAMessage } from '@waha/structures/responses.dto';
import { EnsureSeconds } from '@waha/utils/timehelper';
import { MessageAnyHandler } from '@waha/apps/chatwoot/consumers/waha/message.any';
import { MessageReportInfo } from '@waha/apps/chatwoot/consumers/waha/base';
import { MessageAckEmoji } from '@waha/apps/chatwoot/emoji';
import { SortOrder } from '@waha/structures/pagination.dto';
import {
ArrayPaginator,
PaginatorParams,
} from '@waha/apps/app_sdk/waha/Paginator';
import { EngineHelper } from '@waha/apps/chatwoot/waha/engines';
import { IgnoreJidConfig, isNullJid, JidFilter } from '@waha/core/utils/jids';
import { ErrorRenderer } from '@waha/apps/chatwoot/error/ErrorRenderer';
import { IsCommandsChat } from '@waha/apps/chatwoot/client/ids';
import { CommandPrefix } from '@waha/apps/chatwoot/cli';
import { QueueManager } from '@waha/apps/chatwoot/services/QueueManager';
import { addUnique } from '@waha/utils/list';
export enum ChatID {
ALL = 'all',
SUMMARY = 'summary',
}
export type MessagesPullOptions = {
chat: string;
batch: number;
progress: number | null;
period: {
start: number;
end: number;
};
force: boolean;
pause: boolean;
timeout: {
media: number;
};
media: boolean;
ignore: IgnoreJidConfig;
resolveConversations: boolean;
};
interface Progress {
ok: number;
exists: number;
ignored: number;
errors: number;
chats: string[];
messages: {
first?: number;
last?: number;
};
// Conversations
conversations: number[];
conversationResolve: {
success: number;
error: number;
};
}
const NullProgress: Progress = {
ok: 0,
exists: 0,
ignored: 0,
errors: 0,
chats: [],
messages: {
first: 0,
last: 0,
},
conversations: [],
conversationResolve: {
success: 0,
error: 0,
},
};
function total(progress: Progress) {
return progress.ok + progress.exists + progress.ignored + progress.errors;
}
/**
* Placeholder task consumer for pulling WhatsApp messages.
* Throws until the implementation is completed.
*/
@Processor(QueueName.TASK_MESSAGES_PULL, { concurrency: JOB_CONCURRENCY })
export class TaskMessagesPullConsumer extends ChatWootTaskConsumer {
constructor(
manager: SessionManager,
log: PinoLogger,
rmutex: RMutexService,
protected queueManager: QueueManager,
) {
super(manager, log, rmutex, TaskMessagesPullConsumer.name);
}
protected ErrorHeaderKey(): TKey | null {
return null;
}
protected async Process(
container: DIContainer,
job: Job,
signal: AbortSignal,
) {
const options: MessagesPullOptions = job.data.options;
const locale = container.Locale();
const conversation = this.conversationForReport(container, job.data.body);
const waha = container.WAHASelf();
const session = new WAHASessionAPI(job.data.session, waha);
const handler = new MessagesPullHandler(
this.queueManager,
container,
signal,
container.Logger(),
conversation,
session,
locale,
);
await handler.start(options, job);
job = await handler.handle(options, job);
return await handler.end(options, job);
}
}
class MessagesPullHandler {
private activity: TaskActivity;
constructor(
protected queueManager: QueueManager,
protected container: DIContainer,
protected signal: AbortSignal,
protected logger: ILogger,
conversation: Conversation,
protected session: WAHASessionAPI,
protected l: Locale,
) {
this.activity = new TaskActivity(l, conversation);
}
/**
* Calculate summary progress including current task and direct children
*/
protected async summaryProgress(job: Job): Promise<Progress> {
const current = lodash.merge({}, NullProgress, job.progress);
const childrenValues = await job.getChildrenValues();
const values: Progress[] = [current, ...Object.values(childrenValues)];
let progress = lodash.merge({}, NullProgress);
// Result
for (const p of values) {
progress.ok += p.ok;
progress.exists += p.exists;
progress.ignored += p.ignored;
progress.errors += p.errors;
progress.conversationResolve.success += p.conversationResolve.success;
progress.conversationResolve.error += p.conversationResolve.error;
}
// Chats
progress.chats = lodash.uniq(lodash.flatten(values.map((p) => p.chats)));
// Conversations
progress.conversations = lodash.uniq(
lodash.flatten(values.map((p) => p.conversations)),
);
// Messages timestamps
progress.messages.first = lodash.min(
values.map((p) => p.messages.first || Infinity),
);
if (progress.messages.first === Infinity) {
progress.messages.first = undefined;
}
progress.messages.last = lodash.max(
values.map((p) => p.messages.last || 0),
);
if (progress.messages.last === 0) {
progress.messages.last = undefined;
}
return progress;
}
async start(options: MessagesPullOptions, job: Job) {
const { ignored, processed, unprocessed, failed } =
await job.getDependenciesCount();
const total = ignored + processed + unprocessed + failed;
const hasChildren = total > 0;
if (hasChildren) {
return;
}
// Perform some actions before any job started
if (options.pause) {
await this.queueManager.pause();
await this.activity.queue(true);
}
}
/**
* Perform actions after all jobs are done
*/
async end(options: MessagesPullOptions, job: Job) {
if (job.parentKey) {
// Not end yet, have parent tasks to process
return await this.summaryProgress(job);
}
// Resume queues if paused
if (options.pause) {
await this.queueManager.resume();
await this.activity.queue(false);
}
// Progress summary
const progress = await this.summaryProgress(job);
await job.updateProgress(progress);
// Resolve conversations
if (options.resolveConversations) {
await this.resolveConversations(progress);
await job.updateProgress(progress);
}
// Report completed
await this.activity.completed(progress, options);
return progress;
}
protected async resolveConversations(progress: Progress) {
const service = this.container.ConversationService();
for (const conversationId of progress.conversations) {
try {
await service.resolve(conversationId);
progress.conversationResolve.success += 1;
} catch (err) {
progress.conversationResolve.error += 1;
}
}
}
async handle(options: MessagesPullOptions, job: Job): Promise<Job> {
if (options.chat === ChatID.SUMMARY) {
// Do nothing for "summary" chat
return job;
}
const jids = new JidFilter(options.ignore);
let progress = lodash.merge({}, NullProgress, job.progress);
const batch = options.batch ?? 100;
this.logger.debug(
`Pulling messages for session ${job.data.session} with batch size ${batch}...`,
);
if (lodash.isEqual(progress, NullProgress)) {
await this.activity.started(job, options);
}
// Handler
const container = this.container;
const info = new MessageReportInfo();
const handler = new MessageAnyHistoryHandler(
job,
container.MessageMappingService(),
container.ContactConversationService(),
container.Logger(),
info,
this.session,
container.Locale(),
container.WAHASelf(),
);
handler.force = options.force;
// Messages
const lte = job.timestamp - options.period.end;
const gte = job.timestamp - options.period.start;
const filters: GetChatMessagesFilter = {
'filter.timestamp.lte': EnsureSeconds(lte),
'filter.timestamp.gte': EnsureSeconds(gte),
};
const query: GetChatMessagesQuery = {
limit: batch,
offset: 0,
downloadMedia: false,
sortBy: MessageSortField.TIMESTAMP,
sortOrder: SortOrder.ASC,
};
const params: PaginatorParams = {
processed: total(progress),
};
const paginator = new ArrayPaginator<WAMessage>(params);
let messages = paginator.iterate((processed: number) => {
query.offset = processed;
return this.session.getMessages(options.chat, query, filters, {
signal: this.signal,
});
});
messages = EngineHelper.IterateMessages(messages);
const all = options.chat == ChatID.ALL;
for await (let message of messages) {
this.signal.throwIfAborted();
//
// Show progress
//
const thetotal = total(progress);
if (options.progress && thetotal) {
if (thetotal % options.progress == 0) {
await this.activity.progress(progress, options);
}
}
// Process
try {
progress.messages.first = progress.messages.first || message.timestamp;
progress.messages.last = message.timestamp;
if (all && !jids.include(EngineHelper.ChatID(message))) {
progress.ignored += 1;
continue;
}
if (isNullJid(EngineHelper.ChatID(message))) {
progress.ignored += 1;
continue;
}
if (
options.media &&
message.hasMedia &&
(await handler.ShouldProcessMessage(message))
) {
// Fetch media for the message
let signal = AbortSignal.timeout(options.timeout.media);
signal = AbortSignal.any([signal, this.signal]);
message = await this.session.getMessageById('all', message.id, true, {
signal: signal,
});
}
const chatwoot = await handler.handle(message);
if (chatwoot) {
progress.ok += 1;
addUnique(progress.chats, EngineHelper.ChatID(message));
if (info.conversationId) {
addUnique(progress.conversations, info.conversationId);
}
} else {
progress.exists += 1;
}
} catch (error) {
const renderer = new ErrorRenderer();
const text = renderer.text(error);
this.logger.error(`Error: ${text}`);
this.logger.error(`Message:\n${JSON.stringify(message, null, 2)}`);
try {
const data = renderer.data(error);
this.logger.error(JSON.stringify(data, null, 2));
} catch (err) {
this.logger.error(
`Error occurred while login details for error: ${err}`,
);
}
progress.errors += 1;
}
// update progress before signal fails
await job.updateProgress(progress);
}
job.progress = progress;
return job;
}
}
export class TaskActivity {
constructor(
private l: Locale,
private conversation: Conversation,
) {}
public async queue(paused: boolean) {
let msg: string;
if (paused) {
msg = this.l.r('cli.cmd.queue.paused');
} else {
msg = this.l.r('cli.cmd.queue.resumed');
}
await this.conversation.activity(msg);
}
public async details(data) {
await this.conversation.activity(
this.l.r('task.messages.details', {
prefix: IsCommandsChat(data.body) ? '' : CommandPrefix,
}),
);
}
public async started(job: Job, options: MessagesPullOptions) {
await this.conversation.activity(
this.l.r('task.messages.started', {
chat: options.chat,
period: period(options),
}),
);
}
public async progress(progress: Progress, options: MessagesPullOptions) {
const format: Intl.DateTimeFormatOptions = {
month: 'short',
day: 'numeric',
year: 'numeric',
};
await this.conversation.activity(
this.l.r('task.messages.progress', {
progress: progress,
chat: options.chat,
messages: {
first: this.l.FormatTimestampOpts(
progress.messages.first,
format,
false,
),
last: this.l.FormatTimestampOpts(
progress.messages.last,
format,
false,
),
},
}),
);
}
/**
* Final report
*/
public async completed(progress, options) {
const msg = this.l.r('task.messages.completed', {
progress: progress,
period: period(options),
chat: options.chat,
});
await this.conversation.activity(msg);
}
}
class MessageAnyHistoryHandler extends MessageAnyHandler {
public force: boolean = false;
public shouldLogUnsupported = true;
protected get historyMessage() {
return true;
}
protected get shouldAddFromTag() {
return false;
}
protected get delayFromMeAPI() {
return 0;
}
async ShouldProcessMessage(payload: WAMessage) {
if (this.force) {
return true;
}
return await super.ShouldProcessMessage(payload);
}
}
function period(options: MessagesPullOptions) {
const start = options.period.start;
const end = options.period.end;
if (end == 0) {
return ms(start);
}
return `${ms(start)}-${ms(end)}`;
}
export function MessagesPullStatusMessage(
l: Locale,
job: Job,
state: JobState | 'unknown',
) {
const progress = lodash.merge({}, NullProgress, job.progress);
const details = JobLink(job);
const options = job.data.options as MessagesPullOptions;
const payload = {
options: options,
chat: options.chat,
chats: progress.chats.length,
period: period(options),
error: state === 'failed' || state === 'unknown' || progress.errors > 0,
state: lodash.capitalize(state ?? 'unknown'),
progress: progress,
details: details,
messages: {
first: l.FormatTimestamp(progress.messages.first, false),
last: l.FormatTimestamp(progress.messages.last, false),
},
};
return l.r('task.messages.status', payload);
}
+88 -32
View File
@@ -13,7 +13,7 @@ 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 } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf, WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASelf, WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import {
AppRepository,
ChatwootMessage,
@@ -25,16 +25,15 @@ import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { MessageSource, WAMessageBase } from '@waha/structures/responses.dto';
import {
WAHAWebhook,
WAHAWebhookMessageRevoked,
} from '@waha/structures/webhooks.dto';
import { sleep } from '@waha/utils/promiseTimeout';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { isJidBroadcast, isJidGroup, toCusFormat } from '@waha/core/utils/jids';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
import { EnsureSeconds } from '@waha/utils/timehelper';
import { CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS } from '@waha/apps/chatwoot/env';
export function ListenEventsForChatWoot() {
return [
@@ -177,7 +176,7 @@ export interface IMessageInfo {
onMessageType(type: MessageType): void;
}
class MessageReportInfo implements IMessageInfo {
export class MessageReportInfo implements IMessageInfo {
public conversationId: number | null = null;
public type: MessageType | null = null;
@@ -212,10 +211,65 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
payload: Payload,
): Promise<ChatWootMessagePartial>;
protected get historyMessage() {
return false;
}
protected finalizeContent(content: string, payload: Payload): string {
if (!content) {
return content;
}
if (!payload.timestamp) {
return content;
}
if (!this.historyMessage) {
// Check if the message is recent enough to be considered current
const now = EnsureSeconds(Date.now() / 1000);
const messageAge = now - EnsureSeconds(payload.timestamp);
const recent = messageAge <= CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS;
if (recent) {
return content;
}
}
const date = this.l.ParseTimestamp(payload.timestamp);
if (!date) {
return content;
}
const timestamp = this.l.FormatHumanDate(date);
return this.l.r('whatsapp.history.message.wrapper', {
content: content,
payload: payload,
history: this.historyMessage,
timestamp: timestamp,
});
}
abstract getReplyToWhatsAppID(payload: Payload): string | undefined;
async handle(data: WAHAWebhook<Payload>) {
const payload = data.payload;
protected get delayFromMeAPI() {
return 3_000;
}
/**
* Weather to add a tag to messages sent from me like via API or WhatsApp
*/
protected get shouldAddFromTag() {
return true;
}
async ShouldProcessMessage(payload: Payload): Promise<boolean> {
const key = parseMessageIdSerialized(payload.id);
const chatwoot = await this.mappingService.getChatWootMessage({
chat_id: toCusFormat(key.remoteJid),
message_id: key.id,
});
if (chatwoot) {
return false;
}
return true;
}
async handle(payload: Payload): Promise<conversation_message_create | null> {
// Find the type as soon as possible for error reporting
const type = payload.fromMe ? MessageType.OUTGOING : MessageType.INCOMING;
this.info.onMessageType(type);
@@ -224,28 +278,25 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
// It also handles messages fromMe and ChatWoot (but does not if send via API)
if (payload.fromMe && payload.source === MessageSource.API) {
// Sleep a few seconds to save it to a database
await sleep(3_000);
await sleep(this.delayFromMeAPI);
}
const key = parseMessageIdSerialized(payload.id);
const chatwoot = await this.mappingService.getChatWootMessage({
chat_id: toCusFormat(key.remoteJid),
message_id: key.id,
});
if (chatwoot) {
this.logger.info(
`Message '${payload.id}' already in ChatWoot: conversation.id=${chatwoot.conversation_id}, message.id=${chatwoot.message_id}`,
);
return;
if (!(await this.ShouldProcessMessage(payload))) {
const log = `Skipping existing message '${payload.id}' from WhatsApp`;
this.logger.debug(log);
return null;
}
const contactInfo = WhatsAppContactInfo(this.session, payload.from, this.l);
const contactInfo = WhatsAppContactInfo(
this.session,
EngineHelper.ChatID(payload as any),
this.l,
);
const conversation = await this.repo.ConversationByContact(contactInfo);
this.info.onConversationId(conversation.conversationId);
const message = await this.buildChatWootMessage(payload);
const response = await conversation.send(message);
this.logger.info(
this.logger.debug(
`Created message as '${message.message_type}' from WhatsApp: ${response.id}`,
);
await this.saveMapping(response, payload);
@@ -280,22 +331,26 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
let content = message.content;
// Format the content if the message from me
if (payload.fromMe && payload.source === MessageSource.APP) {
const key = TKey.MESSAGE_FROM_WHATSAPP;
content = this.l.key(key).render({ text: content });
} else if (payload.fromMe && payload.source === MessageSource.API) {
const key = TKey.MESSAGE_FROM_API;
content = this.l.key(key).render({ text: content });
if (payload.fromMe && this.shouldAddFromTag) {
if (payload.source === MessageSource.APP) {
const key = TKey.MESSAGE_FROM_WHATSAPP;
content = this.l.key(key).render({ text: content });
} else if (payload.source === MessageSource.API) {
const key = TKey.MESSAGE_FROM_API;
content = this.l.key(key).render({ text: content });
}
}
const chatId = payload.from;
const chatId = EngineHelper.ChatID(payload);
// Add participant name to group messages
const manyParticipants = isJidGroup(chatId) || isJidBroadcast(chatId);
if (!payload.fromMe && manyParticipants) {
const key = parseMessageIdSerialized(payload.id, true);
let participant = toCusFormat(key.participant);
const contact: any = await this.session.getContact(key.participant);
let participant = toCusFormat(
key.participant || (payload as any)._data.participant,
);
const contact: any = await this.session.getContact(participant);
const name = contact?.name || contact?.pushName || contact?.pushname;
if (name) {
participant = `${name} (${participant})`;
@@ -315,6 +370,7 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
);
const type = payload.fromMe ? MessageType.OUTGOING : MessageType.INCOMING;
content = this.finalizeContent(content, payload);
return {
content: content,
message_type: type,
@@ -334,7 +390,7 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
return;
}
const chatwoot = await this.mappingService.getChatWootMessage({
chat_id: payload.from,
chat_id: EngineHelper.ChatID(payload),
message_id: replyToWhatsAppID,
});
return chatwoot?.message_id;
+38 -18
View File
@@ -10,15 +10,16 @@ import {
} from '@waha/apps/chatwoot/consumers/waha/base';
import { WhatsAppContactInfo } from '@waha/apps/chatwoot/contacts/WhatsAppContactInfo';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents, WAMessageAck } from '@waha/structures/enums.dto';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { WAHAWebhookMessageAck } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { isJidCusFormat } from '@waha/utils/wa';
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { ShouldMarkAsReadInChatWoot } from '@waha/apps/chatwoot/consumers/waha/message.ack.utils';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { MessageMappingService } from '@waha/apps/chatwoot/storage';
@Processor(QueueName.WAHA_MESSAGE_ACK, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
@@ -30,18 +31,8 @@ export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
super(manager, log, rmutex, 'WAHAMessageAckConsumer');
}
ShouldProcess(event: WAHAWebhookMessageAck): boolean {
// Mark as seen only if it's DM
// Ignore groups and other multiple participants chats
const chatId = this.GetChatId(event);
if (!isJidCusFormat(chatId) && !isLidUser(chatId)) {
return false;
}
// Only READ and PLAYED
const payload = event.payload;
return (
payload.ack === WAMessageAck.READ || payload.ack == WAMessageAck.PLAYED
);
ShouldProcess(event: any): boolean {
return ShouldMarkAsReadInChatWoot(event);
}
GetChatId(event: WAHAWebhookMessageAck): string {
@@ -57,18 +48,26 @@ export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
const session = new WAHASessionAPI(event.session, container.WAHASelf());
const handler = new MessageAckHandler(
container.ContactConversationService(),
container.MessageMappingService(),
container.Logger(),
info,
session,
container.Locale(),
);
await handler.handle(event);
try {
await handler.handle(event);
} catch (e) {
// TODO: Investigate errors
// https://github.com/devlikeapro/waha/issues/1492
this.logger.error(e);
}
}
}
class MessageAckHandler {
constructor(
private readonly contactConversationService: ContactConversationService,
protected mappingService: MessageMappingService,
private readonly logger: ILogger,
private readonly info: IMessageInfo,
private readonly session: WAHASessionAPI,
@@ -77,13 +76,34 @@ class MessageAckHandler {
async handle(event: WAHAWebhookMessageAck): Promise<void> {
const payload = event.payload;
const key = parseMessageIdSerialized(payload.id);
const chatwoot = await this.mappingService.getChatWootMessage({
chat_id: null,
message_id: key.id,
});
// No chatwoot message found, so we don't mark it as read
// Filters out old messages and some service ack messages
if (!chatwoot) {
return;
}
const contactInfo = WhatsAppContactInfo(
this.session,
payload.from,
this.locale,
);
const conversation =
await this.contactConversationService.ConversationByContact(contactInfo);
await this.contactConversationService.FindConversationByContact(
contactInfo,
);
if (!conversation) {
this.logger.debug(
`No suitable conversation found to mark as read for chat.id: ${payload.from}`,
);
return;
}
this.info.onConversationId(conversation.conversationId);
const sourceId = conversation.sourceId;
@@ -0,0 +1,466 @@
import type { WAHAEngine, WAHAEvents } from '@waha/structures/enums.dto';
import { ShouldMarkAsReadInChatWoot } from '@waha/apps/chatwoot/consumers/waha/message.ack.utils';
interface TestParam {
name: string;
expected: boolean;
GOWS: any;
NOWEB: any;
WEBJS: any;
}
/**
* Me - 444444444444 (lid - 144444444444444)
* Participant - 11111111111 (lid - 011111111111111)
* Group - 222222222222222222
*/
const ShouldMarkAsReadCases: TestParam[] = [
{
name: 'Participant read My message (READ ack)',
expected: true,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'true_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '11111111111@s.whatsapp.net',
IsFromMe: false,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: 'read',
MessageSender: '',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: true,
ack: 3,
ackName: 'READ',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAA',
from: '444444444444@c.us',
fromMe: true,
source: 'app',
to: '11111111111@c.us',
body: 'Nn',
hasMedia: false,
media: null,
ack: 3,
ackName: 'READ',
vCards: [],
_data: {
id: {
fromMe: true,
remote: '11111111111@c.us',
id: 'AAA',
_serialized: 'true_11111111111@c.us_AAA',
},
viewed: false,
from: '444444444444@c.us',
to: '11111111111@c.us',
author: '444444444444@c.us',
ack: 3,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
{
name: 'Participant received My message (DEVICE ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'true_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: true,
ack: 2,
ackName: 'DEVICE',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '11111111111:3@s.whatsapp.net',
IsFromMe: false,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: '',
MessageSender: '',
},
},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: true,
ack: 2,
ackName: 'DEVICE',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAA',
from: '444444444444@c.us',
fromMe: true,
source: 'app',
to: '11111111111@c.us',
body: 'Nn',
hasMedia: false,
media: null,
ack: 2,
ackName: 'DEVICE',
vCards: [],
_data: {
id: {
fromMe: true,
remote: '11111111111@c.us',
id: 'AAA',
_serialized: 'true_11111111111@c.us_AAA',
},
from: '444444444444@c.us',
to: '11111111111@c.us',
author: '444444444444@c.us',
ack: 2,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
{
name: 'Me read Participant message on another device (READ ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'false_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: false,
ack: 3,
ackName: 'READ',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '444444444444@s.whatsapp.net',
IsFromMe: true,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: 'read',
MessageSender: '11111111111@s.whatsapp.net',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: '11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: false,
ack: 3,
ackName: 'READ',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: null,
},
{
name: 'Participant read My Group Message (READ ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '011111111111111@lid',
participant: '011111111111111@lid',
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
Chat: '222222222222222222@g.us',
Sender: '011111111111111@lid',
IsFromMe: false,
IsGroup: true,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
Type: 'read',
MessageSender: '',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '222222222222222222@g.us',
participant: '011111111111111@lid',
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
key: {
remoteJid: '222222222222222222@g.us',
id: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
fromMe: true,
participant: '011111111111111@lid',
},
receipt: {
userJid: '011111111111111@lid',
},
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '222222222222222222@g.us',
participant: '011111111111111@lid',
fromMe: false,
ack: 3,
ackName: 'READ',
_data: {
tag: 'receipt',
attrs: {
from: {
$1: {
type: 0,
user: '222222222222222222',
server: 'g.us',
},
},
type: 'read',
id: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
participant: {
$1: {
type: 1,
user: '011111111111111',
device: 0,
domainType: 1,
},
},
},
content: null,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
];
describe('ShouldMarkAsReadInChatWoot', () => {
const engines = ['GOWS', 'NOWEB', 'WEBJS'];
for (const engine of engines) {
describe(engine, () => {
for (const param of ShouldMarkAsReadCases) {
const name = `[${param.expected}] ${param.name}`;
const event = param[engine];
const testfn = event ? test : test.skip;
const expected = param.expected;
testfn(name, () => {
// Test
const result = ShouldMarkAsReadInChatWoot(event);
expect(result).toBe(expected);
});
}
});
}
});
@@ -0,0 +1,32 @@
import type { WAHAWebhookMessageAck } from '@waha/structures/webhooks.dto';
import { isJidCusFormat } from '@waha/utils/wa';
import { WAMessageAck } from '@waha/structures/enums.dto';
import { isLidUser, toCusFormat } from '@waha/core/utils/jids';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
export function ShouldMarkAsReadInChatWoot(
event: WAHAWebhookMessageAck,
): boolean {
// Mark as seen only if it's DM
// Ignore groups and other multiple participants chats
const chatId = EngineHelper.ChatID(event.payload);
if (!isJidCusFormat(chatId) && !isLidUser(chatId)) {
return false;
}
const payload = event.payload;
// Only READ and PLAYED
const read = payload.ack === WAMessageAck.READ;
const played = payload.ack == WAMessageAck.PLAYED;
if (!read && !played) {
return false;
}
// Only process when OUR message (fromMe: true) was read by recipient
if (!payload.fromMe) {
return false;
}
// Mark ChatWoot conversation as read when recipient reads our message
return true;
}
@@ -8,7 +8,7 @@ import {
IMessageInfo,
MessageBaseHandler,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -24,11 +24,15 @@ import {
LocationMessage,
MessageToChatWootConverter,
PollMessage,
ListMessage,
ShareContactMessage,
TextMessage,
UnsupportedMessage,
AlbumMessage,
resolveProtoMessage,
} from '@waha/apps/chatwoot/messages/to/chatwoot';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
import { HasMediaWithNoMediaMessage } from '@waha/apps/chatwoot/messages/to/chatwoot/HasMediaWithNoMediaMessage';
@Processor(QueueName.WAHA_MESSAGE_ANY, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
@@ -41,7 +45,7 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
}
GetChatId(event: WAHAWebhookMessageAny): string {
return event.payload.from;
return EngineHelper.ChatID(event.payload);
}
async Process(
@@ -61,17 +65,19 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
container.Locale(),
container.WAHASelf(),
);
return await handler.handle(event);
return await handler.handle(event.payload);
}
}
class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
export class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
public shouldLogUnsupported: boolean = false;
protected async getMessage(
payload: WAMessage,
): Promise<ChatWootMessagePartial> {
let converter: MessageToChatWootConverter;
let msg: ChatWootMessagePartial;
const protoMessage = resolveProtoMessage(payload);
const protoMessage = resolveProtoMessage(payload._data);
// Check for Facebook Ad first - but let it use the normal flow later
converter = new FacebookAdMessage(this.l, this.logger);
@@ -80,7 +86,7 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
return msg;
}
converter = new TextMessage(this.l, this.logger, this.waha);
converter = new TextMessage(this.l, this.logger, this.waha, this.job);
msg = await converter.convert(payload, null);
if (msg) {
return msg;
@@ -104,6 +110,12 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
return msg;
}
converter = new ListMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
converter = new EventMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
@@ -116,8 +128,25 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
return msg;
}
converter = new AlbumMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
converter = new HasMediaWithNoMediaMessage(this.l, this.job);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
converter = new UnsupportedMessage(this.l, this.job);
msg = await converter.convert(payload, protoMessage);
if (this.shouldLogUnsupported) {
this.logger.warn(
`UnsupportedMessage:\n${JSON.stringify(payload, null, 2)}`,
);
}
return msg;
}
@@ -126,6 +155,9 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
if (!replyTo) {
return undefined;
}
if (!replyTo.id) {
return undefined;
}
const key = parseMessageIdSerialized(replyTo.id, true);
return key.id;
}
@@ -18,12 +18,13 @@ import {
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { WAHASessionAPI } from '../../session/WAHASelf';
import { WAHASessionAPI } from '../../../app_sdk/waha/WAHASelf';
import {
MessageEdited,
MessageToChatWootConverter,
resolveProtoMessage,
} from '@waha/apps/chatwoot/messages/to/chatwoot';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
@Processor(QueueName.WAHA_MESSAGE_EDITED, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
@@ -36,7 +37,7 @@ export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
}
GetChatId(event: WAHAWebhookMessageEdited): string {
return event.payload.from;
return EngineHelper.ChatID(event.payload);
}
async Process(
@@ -56,7 +57,7 @@ export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
container.Locale(),
container.WAHASelf(),
);
return await handler.handle(event);
return await handler.handle(event.payload);
}
}
@@ -64,7 +65,7 @@ class MessageEditedHandler extends MessageBaseHandler<WAMessageEditedBody> {
protected async getMessage(
payload: WAMessageEditedBody,
): Promise<ChatWootMessagePartial> {
const protoMessage = resolveProtoMessage(payload);
const protoMessage = resolveProtoMessage(payload._data);
const converter: MessageToChatWootConverter = new MessageEdited(this.l);
return converter.convert(payload, protoMessage);
}
@@ -9,16 +9,17 @@ import {
IMessageInfo,
} 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 { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { WAHAEngine, WAHAEvents } from '@waha/structures/enums.dto';
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';
import { EngineHelper } from '@waha/apps/chatwoot/waha';
@Processor(QueueName.WAHA_MESSAGE_REACTION, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
@@ -31,7 +32,13 @@ export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
}
GetChatId(event: WAHAWebhookMessageReaction): string {
return event.payload.from;
if (event.environment?.engine == WAHAEngine.WEBJS) {
// chat in "to" field for WEBJS for message.reaction
// probably we need to set it to "from"
// but for backward compatability we do this
return event.payload.to;
}
return EngineHelper.ChatID(event.payload);
}
async Process(
@@ -51,7 +58,7 @@ export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
container.Locale(),
container.WAHASelf(),
);
return await handler.handle(event);
return await handler.handle(event.payload);
}
}
@@ -11,8 +11,8 @@ import {
IMessageInfo,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionStatusEmoji } from '@waha/apps/chatwoot/SessionStatusEmoji';
import { WAHASelf } from '@waha/apps/app_sdk/waha/WAHASelf';
import { SessionStatusEmoji } from '@waha/apps/chatwoot/emoji';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents, WAHASessionStatus } from '@waha/structures/enums.dto';
@@ -2,7 +2,7 @@ import { public_contact_create_update_payload as Contact } from '@figuro/chatwoo
import { ContactInfo } from '@waha/apps/chatwoot/client/ContactConversationService';
import { AttributeKey } from '@waha/apps/chatwoot/const';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
import { Channel } from '@waha/structures/channels.dto';
import { CacheAsync } from '@waha/utils/Cache';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@@ -91,6 +91,9 @@ class LidContactInfo extends ChatContactInfo {
@CacheAsync()
async jid() {
const pn = await this.session.findPNByLid(this.chatId);
if (!pn) {
return null;
}
return new JidContactInfo(this.session, pn, this.locale);
}
@@ -99,7 +102,7 @@ class LidContactInfo extends ChatContactInfo {
if (jid) {
return await jid.AvatarUrl();
}
return null;
return await this.session.getChatPicture(this.chatId);
}
@CacheAsync()
@@ -109,7 +112,6 @@ class LidContactInfo extends ChatContactInfo {
if (jid) {
attributes = await jid.Attributes();
}
attributes[AttributeKey.WA_CHAT_ID] = this.chatId;
attributes[AttributeKey.WA_LID] = this.chatId;
return attributes;
}
+4 -3
View File
@@ -15,7 +15,7 @@ import {
} from '@waha/apps/chatwoot/dto/config.dto';
import { ChatWootErrorReporter } from '@waha/apps/chatwoot/error/ChatWootErrorReporter';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import { WAHASelf } from '@waha/apps/app_sdk/waha/WAHASelf';
import {
ChatwootMessageRepository,
MessageMappingRepository,
@@ -109,7 +109,7 @@ export class DIContainer {
* @returns ContactService instance
*/
@CacheSync()
private ContactService(): ContactService {
public ContactService(): ContactService {
return new ContactService(
this.config,
this.AccountAPI(),
@@ -133,7 +133,7 @@ export class DIContainer {
* @returns ConversationService instance
*/
@CacheSync()
private ConversationService(): ConversationService {
public ConversationService(): ConversationService {
return new ConversationService(
this.config,
this.AccountAPI(),
@@ -212,6 +212,7 @@ export class DIContainer {
linkPreview: LinkPreview.OFF,
commands: {
server: true,
queue: false,
},
conversations: {
sort: ConversationSort.created_newest,
+4
View File
@@ -20,6 +20,10 @@ export const DEFAULT_LOCALE = 'en-US';
export class ChatWootCommandsConfig {
@IsBoolean()
server: boolean = true;
@IsOptional()
@IsBoolean()
queue?: boolean = false;
}
export enum LinkPreview {
+37
View File
@@ -0,0 +1,37 @@
import { WAHASessionStatus, WAMessageAck } from '@waha/structures/enums.dto';
export function SessionStatusEmoji(status: WAHASessionStatus): string {
switch (status) {
case WAHASessionStatus.STOPPED:
return '⚠️';
case WAHASessionStatus.STARTING:
return '⏳';
case WAHASessionStatus.SCAN_QR_CODE:
return '⚠️';
case WAHASessionStatus.WORKING:
return '🟢';
case WAHASessionStatus.FAILED:
return '🛑';
default:
return '❓';
}
}
export function MessageAckEmoji(ack: WAMessageAck) {
switch (ack) {
case WAMessageAck.ERROR:
return '❌';
case WAMessageAck.PENDING:
return '⏳';
case WAMessageAck.SERVER:
return '✔️';
case WAMessageAck.DEVICE:
return '✔️';
case WAMessageAck.READ:
return '✅';
case WAMessageAck.PLAYED:
return '✅';
default:
return '❔';
}
}
+10
View File
@@ -0,0 +1,10 @@
export const CHATWOOT_COMMAND_PREFIX =
process.env.WAHA_CHATWOOT_COMMAND_PREFIX || 'wa/';
export const CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS =
parseInt(process.env.WAHA_CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS) || 600;
export const APPS_CHATWOOT_LANGUAGES_FOLDER =
process.env.WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER?.trim();
export const TZ = process.env.TZ;
@@ -39,6 +39,12 @@ export class ChatWootErrorReporter {
}
const template = this.l.key(TKey.JOB_REPORT_ERROR);
const nextDelay = NextAttemptDelayInWholeSeconds(this.job);
if (nextDelay) {
// TODO: Add a way to enable it back in config
// https://github.com/devlikeapro/waha/issues/1395
// There's retries more left - ignore it for now
return;
}
const attempts = {
current: this.job.attemptsMade + 1,
max: this.job.opts?.attempts || 1,
@@ -77,6 +83,10 @@ export class ChatWootErrorReporter {
type: MessageType,
replyTo?: number,
): Promise<void> {
// TODO: Add a way to enable it back in config
// https://github.com/devlikeapro/waha/issues/1395
return null;
const template = this.l.key(TKey.JOB_REPORT_SUCCEEDED);
const attempts = {
current: this.job.attemptsMade + 1,
+2 -1
View File
@@ -1,6 +1,7 @@
import * as path from 'path';
import { YamlLocaleLoader } from '@waha/apps/chatwoot/i18n/loader';
import { I18N } from '@waha/apps/chatwoot/i18n/i18n';
import { APPS_CHATWOOT_LANGUAGES_FOLDER } from '@waha/apps/chatwoot/env';
const i18n = new I18N();
const localesDir = path.join(__dirname, 'locales');
@@ -9,7 +10,7 @@ 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();
const additional = APPS_CHATWOOT_LANGUAGES_FOLDER;
if (additional) {
const resolved = path.resolve(additional);
i18n.load(new YamlLocaleLoader(resolved, 'yml').load());
+86
View File
@@ -0,0 +1,86 @@
import { i18n } from '@waha/apps/chatwoot/i18n/index';
const enLocale = i18n.locale('en-US');
// @ts-ignore
enLocale.strings.datetime.timeZone = 'America/New_York';
const ptBrLocale = i18n.locale('pt-BR');
// @ts-ignore
ptBrLocale.strings.datetime.timeZone = 'America/Sao_Paulo';
describe('Locale', () => {
describe('FormatHumanDate', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('formats dates occurring today with the Today prefix', () => {
const now = new Date('2024-03-15T10:30:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(new Date(now));
expect(result).toBe('Today, Mar 15 • 06:30 AM EDT');
});
it('formats dates occurring yesterday with the Yesterday prefix', () => {
const now = new Date('2024-03-15T10:30:00Z');
const yesterday = new Date('2024-03-14T10:30:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(yesterday);
expect(result).toBe('Yesterday, Mar 14 • 06:30 AM EDT');
});
it('formats dates from the current year without the year', () => {
const now = new Date('2024-05-01T09:00:00Z');
const date = new Date('2024-02-10T14:45:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(date);
expect(result).toBe('Sat, Feb 10 • 09:45 AM EST');
});
it('formats dates from previous years including the year', () => {
const now = new Date('2024-05-01T09:00:00Z');
const date = new Date('2023-07-10T12:15:00Z');
jest.setSystemTime(now);
const result = enLocale.FormatHumanDate(date);
expect(result).toBe('Mon, Jul 10, 2023 • 08:15 AM EDT');
});
});
describe('FormatHumanDate (pt-BR)', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('uses localized Today and Yesterday labels', () => {
const now = new Date('2024-03-15T10:30:00Z');
const yesterday = new Date('2024-03-14T10:30:00Z');
jest.setSystemTime(now);
const todayResult = ptBrLocale.FormatHumanDate(now);
const yesterdayResult = ptBrLocale.FormatHumanDate(yesterday);
expect(todayResult).toBe('Hoje, 15 de mar. • 07:30 BRT');
expect(yesterdayResult).toBe('Ontem, 14 de mar. • 07:30 BRT');
});
});
});
+97 -11
View File
@@ -1,10 +1,14 @@
import * as Mustache from 'mustache';
import * as lodash from 'lodash';
import { TemplatePayloads, TKey } from '@waha/apps/chatwoot/i18n/templates';
import Long from 'long';
import { ensureNumber } from '@waha/core/engines/noweb/utils';
import { EnsureMilliseconds } from '@waha/utils/timehelper';
import { TZ } from '@waha/apps/chatwoot/env';
import { isToday, isYesterday, isSameYear } from '@waha/utils/datehelper';
export class Locale {
constructor(private readonly strings: Record<string, string>) {}
constructor(public readonly strings: Record<string, string>) {}
/**
* Return Base Locale
@@ -18,6 +22,11 @@ export class Locale {
return new Template(this.strings[key] || key);
}
r<K extends TKey | string>(key: K, data: TemplatePayloads[K] = null): string {
const template = this.key(key as TKey);
return template.render(data as any);
}
/**
* Overrides the existing strings with the provided strings for the locale.
* Merges the new strings with the current strings.
@@ -50,16 +59,12 @@ export class Locale {
}
}
FormatDatetime(date: Date | null): string | null {
if (!date) {
return null;
}
const options: any = this.strings['datetime'] || {};
options.timeZone = options.timeZone || options.timezone || process.env.TZ;
return date.toLocaleString(this.locale, options);
FormatDatetime(date: Date | null, year): string | null {
const options: any = lodash.clone(this.strings['datetime'] || {});
return this.FormatDatetimeOpts(date, options, year);
}
FormatTimestamp(timestamp: Long | string | number | null): string | null {
ParseTimestamp(timestamp: Long | string | number | null): Date | null {
const value = ensureNumber(timestamp);
if (!value) {
return undefined;
@@ -67,12 +72,93 @@ export class Locale {
if (!Number.isFinite(value)) {
return undefined;
}
const milliseconds = value >= 1e12 ? value : value * 1000;
const milliseconds = EnsureMilliseconds(value);
const date = new Date(milliseconds);
if (Number.isNaN(date.getTime())) {
return undefined;
}
return this.FormatDatetime(date);
return date;
}
FormatTimestamp(
timestamp: Long | string | number | null,
year: boolean = true,
): string | null {
const date = this.ParseTimestamp(timestamp);
return this.FormatDatetime(date, year);
}
/**
* Format date using custom options
*/
FormatDatetimeOpts(
date: Date,
options: Intl.DateTimeFormatOptions,
year: boolean,
) {
options = lodash.cloneDeep(options);
if (!date) {
return null;
}
const opts: any = this.strings['datetime'] || {};
// Copy timezone if any
options.timeZone = opts.timeZone || opts.timezone || TZ;
if (!year && date.getFullYear() === new Date().getFullYear()) {
// Hide year if current year
options.year = undefined;
}
return date.toLocaleDateString(this.locale, options);
}
FormatTimestampOpts(
timestamp: Long | string | number | null,
options: Intl.DateTimeFormatOptions,
year: boolean = true,
): string | null {
const date = this.ParseTimestamp(timestamp);
return this.FormatDatetimeOpts(date, options, year);
}
FormatHumanDate(date: Date): string {
const options: any = lodash.clone(this.strings['datetime'] || {});
const now = new Date();
const today = isToday(date, now);
const yesterday = isYesterday(date, now);
const sameYear = isSameYear(date, now);
const dateOptions: Intl.DateTimeFormatOptions = {
...options,
hour: undefined,
minute: undefined,
second: undefined,
timeZoneName: undefined,
year: sameYear ? undefined : options.year,
weekday: today || yesterday ? undefined : options.weekday,
};
const timeOptions: Intl.DateTimeFormatOptions = {
...options,
year: undefined,
month: undefined,
day: undefined,
weekday: undefined,
};
const dateStr = date.toLocaleDateString(this.locale, dateOptions);
const timeStr = date.toLocaleTimeString(this.locale, timeOptions);
if (today) {
const todayLabel = this.r('Today');
return `${todayLabel}, ${dateStr} • ${timeStr}`;
}
if (yesterday) {
const yesterdayLabel = this.r('Yesterday');
return `${yesterdayLabel}, ${dateStr} • ${timeStr}`;
}
// weekday already included in dateStr
return `${dateStr} • ${timeStr}`;
}
}
+144
View File
@@ -0,0 +1,144 @@
Here’s a complete `AGENTS.md` draft for your i18n workflow 👇
---
# 🌐 AGENTS.md — Locale Maintenance Guide
This document defines how the **i18n Agent** manages, updates, and synchronizes
locale files (e.g. `en-US.yaml`, `pt-BR.yaml`, etc.) across all supported
languages.
---
## 🧩 Overview
Each locale file (e.g. `en-US.yaml`) contains translation keys and values used
throughout the system. The **English (en-US)** file is the **source of truth** —
every other language must mirror its structure, keys, emojis, and formatting.
---
## 🪄 Core Commands
### 1. `update all i18n`
**Purpose:** Ensures all locales are synchronized with the current `en-US.yaml`.
**Steps:**
1. **Start with `en-US.yaml`.**
- Scan all code and templates to verify each key is used.
- Ignore usage warnings for `datetime.*`, flat keys without a dot, and any
`locale.*` entries — they are considered always in use.
- Propagate every key change (add, edit, remove) from `en-US.yaml` to all
other locale files with the corresponding translations so structures stay
aligned.
- Preserve `datetime.locales` in every non-English locale; it pins the
language hint for formatting and must never be dropped.
- If any keys appear **unused**, list them and **ask whether to remove or
keep**.
2. **Check for “ChatWoot” mentions.**
- Any occurrence must be reviewed — the system should not reference
_ChatWoot_ directly.
- Ask:
> “Found ‘ChatWoot’ in key `X.Y.Z`. Should we reword this? If yes, how?”
3. **Validate formatting and structure.**
- Ensure all keys use consistent YAML indentation and ordering.
- Emojis, punctuation, and tags (`{{variable}}`, `**bold**`, etc.) must stay
exactly as in `en-US`.
4. **Synchronize all target languages.**
- For each locale (`es-ES.yaml`, `fr-FR.yaml`, etc.):
- Copy the structure and keys from `en-US.yaml`.
- Translate the values from English → target language.
- **Ignore any manual changes in target files** — always use the English
source.
- Preserve all markdown, emojis, punctuation, and placeholders.
5. **Report summary.**
- Added / updated / removed keys count per language.
---
### 2. `update key.* for i18n`
**Purpose:** Update or add translations for specific keys only.
**Steps:**
1. Match all keys provided in the command (supports wildcard like `key.auth.*`).
2. Update those keys in `en-US.yaml` first.
3. Propagate and translate the updated keys to all other locale files.
4. Do **not** touch unrelated keys or structure.
---
## ⚙️ Translation Rules
| Aspect | Rule |
| -------------- | --------------------------------------------------------------------------------- |
| **Structure** | Must always follow `en-US.yaml`. |
| **Formatting** | Keep emojis, punctuation, capitalization, and markdown exactly as in English. |
| **Tags** | Preserve placeholders (`{{variable}}`) and code blocks. |
| **Priority** | English (`en-US.yaml`) is always the canonical version. |
| **Comments** | If a translation key needs explanation, add a YAML comment above it (not inline). |
---
## 🚫 ChatWoot Mentions
- The word **ChatWoot** must never appear in translations.
- When found, the agent must pause and ask:
> “ChatWoot found in key `X.Y.Z`: `{{string}}`. Should we rephrase it (e.g.,
> ‘Inbox’, ‘Messaging Platform’, or something else)?”
---
## 📘 Terms (reserved)
> This section will define key terminology and translation conventions (e.g.,
> “Contact” vs “Customer”, “Session” vs “Chat”), once finalized.
---
## ⚠️ Exceptions
- Treat `datetime.*` keys as standard infrastructure strings; do not flag them
as unused.
- Always keep `datetime.locales` aligned with the target locale code (e.g.,
`es-ES`); if it is missing, restore it during the sync.
- Keys without a dot (e.g., `UNKNOWN`) and any under `locale.*` are canonical
metadata — skip unused checks for them.
---
## 🧾 Example Workflow
```bash
# Full synchronization
> update all i18n
→ Checking unused keys...
→ Found 3 unused keys: session.old, inbox.archive, legacy.test
Keep or remove? [k/r]
→ Found "ChatWoot" in en-US.yaml: "ChatWoot inbox connected"
Replace? [yes/no]
→ Translating en-US → es-ES, fr-FR, pt-BR...
✅ All i18n files updated and aligned with en-US.yaml
```
---
Would you like me to add an example **YAML structure** and show how the agent
should align keys (e.g., diff between en-US and pt-BR)? It helps clarify exactly
how updates propagate.
+343 -38
View File
@@ -1,4 +1,4 @@
# Arabic translations for ChatWoot integration
# Arabic translations for WhatsApp inbox integration
locale.name: '🇦🇪 العربية (Arabic)'
# رمز اللغة الأساسي - يجب أن يكون محليًا صالحًا وفق BCP 47
@@ -6,23 +6,28 @@ locale.name: '🇦🇪 العربية (Arabic)'
# تستخدمه WAHA لعرض الأرقام والعملات والتاريخ/الوقت
locale.base: 'ar-AE'
app.inbox.contact.name: 'تكامل واتساب (WAHA)'
app.inbox.contact.name: 'تكامل واتساب'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" من date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: ar-AE
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: true
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Dubai'
Today: 'اليوم'
Yesterday: 'أمس'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: تعذر إزالة رسالة من واتسا
job.scheduled.error.header: فشل تنفيذ المهمة المجدولة
message.from.whatsapp: |-
{{{text}}}
*📱 أرسلت من واتساب*
📱 *أرسلت من واتساب*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 أرسلت عبر API*
📱 *أرسلت عبر API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *تم حذف هذه الرسالة في واتساب*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'قائمة البث'
whatsapp.contact.status.name: 'الحالة'
app.connected.message: |-
🔗 **متصل: جلسة واتساب إلى صندوق وارد ChatWoot**
🔗 **متصل: جلسة واتساب إلى صندوق الوارد**
✅ تم توصيل جلسة واتساب **{{{name}}}** بنجاح بصندوق وارد ChatWoot هذا.
✅ تم توصيل جلسة واتساب **{{{name}}}** بنجاح بهذا الصندوق الوارد.
صندوق الوارد هذا مرتبط الآن بـ **حساب واتساب** الخاص بك. يمكنك إرسال واستقبال الرسائل مباشرة من هنا — تمامًا مثل استخدام واتساب، ولكن داخل ChatWoot.
صندوق الوارد هذا مرتبط الآن بـ **حساب واتساب** الخاص بك. يمكنك إرسال واستقبال الرسائل مباشرة من هنا — تمامًا مثل استخدام واتساب، ولكن داخل مساحة العمل.
يمكنك أيضًا إدارة جلسة واتساب المرتبطة بصندوق الوارد هذا. لعرض الإجراءات المتاحة، أرسل **help** إلى هذه المحادثة.
@@ -104,9 +109,9 @@ app.connected.message: |-
لنبدأ! 🚀
app.disconnected.message: |-
🔌 **غير متصل: جلسة واتساب من صندوق وارد ChatWoot**
🔌 **غير متصل: جلسة واتساب من صندوق الوارد**
❌ تم قطع اتصال جلسة واتساب **{{{name}}}** من صندوق وارد ChatWoot هذا.
❌ تم قطع اتصال جلسة واتساب **{{{name}}}** من هذا الصندوق الوارد.
لن تتمكن بعد الآن من إرسال أو استقبال رسائل من واتساب.
@@ -117,34 +122,156 @@ app.updated.message: |-
✅ تم تحديث إعدادات جلسة واتساب **{{{name}}}**.
app.commands.list: |-
يمكنك إدارة **تكامل واتساب** عن طريق إرسال أحد الأوامر التالية:
cli.usage.arguments: |-
[الإجراء]
🖥️ **الجلسة**:
1. **status** - الحصول على حالة الجلسة الحالية.
2. **restart** - إعادة تشغيل الجلسة - سيحاول هذا إعادة توصيل حساب واتساب الخاص بك.
3. **start** - بدء الجلسة.
4. **stop** - إيقاف الجلسة.
5. **logout** - تسجيل الخروج من الجلسة، حتى تتمكن من مسح رمز QR وإصلاح الجلسة.
6. **qr** - الحصول على رمز QR للجلسة.
7. **screenshot** - الحصول على لقطة شاشة للجلسة (متاح فقط في محرك **WEBJS**).
8. **help** - الأوامر المتاحة.
cli.usage.command: |-
[الأمر]
{{#commands.server}}
⚙️ الخادم:
- **server status** - الحصول على معلومات إصدار الخادم وحالته.
- **server reboot** - إعادة تشغيل الخادم بسلاسة.
- **server reboot force** - إعادة تشغيل الخادم بالقوة.
{{/commands.server}}
cli.usage.options: |-
[الخيارات]
أرسل **أمرًا** (غير حساس لحالة الأحرف) أو **رقم أمر** إلى هذه المحادثة.
cli.cmd.disabled: ⛔ **تم تعطيل الأمر `{{command}}`**.
app.commands.server.disabled: ⛔ **تم تعطيل أوامر الخادم**.
يرجى التواصل مع مسؤول النظام لديك.
يرجى الاتصال بالمسؤول الخاص بك.
أرسل **help** للحصول على قائمة الأوامر المتاحة.
cli.cmd.root.description: |-
أدِر **تكامل واتساب** الخاص بك!
cli.cmd.root.sub.session: |-
🖥️ **الجلسة**:
cli.cmd.root.sub.server: |-
🖥️ **الخادم**:
cli.cmd.root.sub.sync: |-
🔄️ **المزامنة**:
cli.cmd.session.status.description: |-
اطّلع على حالة الجلسة الحالية.
cli.cmd.session.restart.description: |-
أعد تشغيل الجلسة.
cli.cmd.session.start.description: |-
ابدأ الجلسة.
cli.cmd.session.stop.description: |-
أوقف الجلسة.
cli.cmd.session.logout.description: |-
سجّل الخروج من الجلسة لمسح رمز QR جديد واستعادة الاتصال.
cli.cmd.session.qr.description: |-
اعرض رمز QR الخاص بالجلسة.
cli.cmd.session.screenshot.description: |-
التقط لقطة شاشة للجلسة (متاح فقط مع محرك **WEBJS**).
cli.cmd.contacts.summary: |-
اسحب جهات اتصال واتساب.
cli.cmd.contacts.description: |-
👤 شغِّل `contacts pull` لبدء المزامنة أو `contacts status` لمتابعة التقدم.
cli.cmd.contacts.action.description: |-
استخدم `start` لبدء سحب جهات الاتصال أو `status` لمراجعة المهمة النشطة.
cli.cmd.contacts.pull.description: |-
ابدأ بسحب جهات اتصال واتساب إلى الصندوق الوارد.
cli.cmd.contacts.pull.option.batch: |-
عدد جهات الاتصال التي يتم سحبها في كل دفعة.
cli.cmd.contacts.pull.option.delay-contact: |-
التأخير بين مزامنة كل جهة اتصال لتجنّب حدود معدل سحب الصور.
cli.cmd.contacts.pull.option.delay-batch: |-
التأخير بين دفعات جهات الاتصال.
cli.cmd.contacts.pull.option.avatar: |-
اختر كيفية سحب الصور الرمزية: `skip` أو `if-missing` أو `update`.
cli.cmd.contacts.pull.option.groups: |-
قم بتضمين جهات اتصال مجموعات واتساب في المزامنة.
cli.cmd.contacts.pull.option.no-attributes: |-
لا تسحب سمات جهة الاتصال إذا كانت موجودة بالفعل (هذا يسرّع العملية).
cli.cmd.contacts.pull.already-running: |-
⏳ مهمة **سحب جهات الاتصال** تعمل بالفعل. انتظر حتى تنتهي أو أوقفها قبل بدء واحدة جديدة.
أرسل `contacts status` للاطلاع على التفاصيل.
cli.cmd.options.job.attempts: |-
عدد مرات إعادة محاولة المهمة قبل اعتبارها فاشلة.
cli.cmd.options.job.timeout: |-
الحد الأقصى لمدة تشغيل المهمة قبل إعادة المحاولة (يدعم قيمًا مثل `30s` أو `5m` أو `1h`).
cli.cmd.contacts.status.description: |-
اعرض حالة مهمة السحب الحالية.
cli.cmd.contacts.status.not-found: |-
ℹ️ لم يتم العثور على مهمة **سحب جهات الاتصال** نشطة. ابدأ واحدة باستخدام `contacts pull`.
cli.cmd.server.description: |-
أدِر الخادم.
cli.cmd.server.status.description: |-
اعرض إصدار الخادم وحالته.
cli.cmd.server.reboot.description: |-
أعد تشغيل الخادم.
cli.cmd.server.reboot.option.force: |-
فرض إعادة تشغيل فورية.
cli.help.usage.title: |-
طريقة الاستخدام: أرسل أمراً إلى المحادثة بالشكل التالي:
cli.help.commands.defaultGroup: |-
**الأوامر:**
cli.help.arguments.title: |-
**المعاملات:**
cli.help.options.defaultGroup: |-
**الخيارات:**
cli.help.globalOptions.title: |-
**الخيارات العامة:**
task.contacts.status: |-
👤 سحب جهات الاتصال: **{{state}}**
- ✅ الناجحة: {{{progress.ok}}} جهة اتصال
- ⚪ تم تخطيها: {{{progress.skipped}}} جهة اتصال
- ⚠️ الأخطاء: {{{progress.errors}}} جهة اتصال
{{#job}}
⌛ الجدول الزمني:
- في قائمة الانتظار: {{{timestamp}}}
{{#processedOn}}
- وقت البدء: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- وقت الانتهاء: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
التفاصيل:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
جارٍ سحب جهات الاتصال...
task.contacts.progress: |-
سحب جهات الاتصال: ✅ {{progress.ok}}، ⚪ {{progress.skipped}}، ⚠️ {{progress.errors}}
app.help.reminder: |-
أرسل **help** للحصول على قائمة الأوامر المتاحة.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ صندوق وارد ChatWoot هذا مرتبط بـ **حساب واتساب** وجاهز لإرسال واستقبال الرسائل!
✅ هذا الصندوق الوارد مرتبط بـ **حساب واتساب** وجاهز لإرسال واستقبال الرسائل!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **صندوق وارد ChatWoot هذا مرتبط بحساب واتساب ولكنه غير جاهز لإرسال واستقبال الرسائل!**
⚠️ **هذا الصندوق الوارد مرتبط بحساب واتساب ولكنه غير جاهز لإرسال واستقبال الرسائل!**
يرجى التحقق من حالة الجلسة ومحاولة إعادة تشغيلها أو تسجيل الخروج ومسح رمز QR مرة أخرى.
app.session.scan.qr.code: |-
⚠️ **امسح رمز QR على هاتفك لربط حساب واتساب بصندوق وارد ChatWoot هذا!**
⚠️ **امسح رمز QR على هاتفك لربط حساب واتساب بهذا الصندوق الوارد!**
app.server.version.and.status: |-
**الإصدار:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **هذا النوع من الرسائل غير مدعوم في ChatWoot.**
⚠️ **هذا النوع من الرسائل غير مدعوم في هذا الصندوق الوارد.**
📱 يرجى فتح **WhatsApp** لعرضه.
التفاصيل: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **تم استلام رسالة ألبوم**
{{#totalCount}}
تم استلام ألبوم يحتوي على **{{totalCount}}** ملف وسائط.
{{#expectedImageCount}}
📸 صور: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 فيديوهات: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
سيتم تسليم ملفات الوسائط في الرسائل التالية.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **إعلان فيسبوك**
@@ -352,3 +522,138 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **رسالة العميل:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: غير معروف
ERROR: خطأ
PENDING: جارٍ الإرسال
SERVER: الخادم
DEVICE: الجهاز
READ: مقروء
PLAYED: مُشغَّل
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **تحتوي الرسالة على وسائط ولكن تعذّر تنزيلها**
📱 يرجى فتح **واتساب** للاطلاع عليها.
{{^content}}
التفاصيل: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **قائمة الانتظار**:'
cli.cmd.messages.summary: سحب رسائل واتساب.
cli.cmd.messages.description:
💬 نفّذ `messages pull` لبدء مزامنة الرسائل أو `messages status` لمتابعة
التقدم.
cli.cmd.messages.action.description:
'`pull` لبدء سحب الرسائل أو `status` لمراجعة المهمة النشطة.'
cli.cmd.messages.pull.option.chat: حصر السحب على معرّف دردشة واتساب محدد.
cli.cmd.messages.pull.option.force:
إجبار صندوق الوارد على إنشاء الرسائل حتى لو كانت رسالة واتساب موجودة بالفعل في
مخطط المحادثة.
cli.cmd.messages.pull.option.batch: عدد الرسائل التي يتم سحبها في كل دفعة.
cli.cmd.messages.pull.option.progress:
الإبلاغ عن التقدم كل عدد N من الرسائل. القيمة 0 لتعطيل التقارير.
cli.cmd.messages.pull.option.no-dm: تخطي الرسائل الخاصة أثناء سحب السجل.
cli.cmd.messages.pull.option.groups: تضمين مجموعات واتساب أثناء سحب السجل.
cli.cmd.messages.pull.option.channels: تضمين قنوات واتساب أثناء سحب السجل.
cli.cmd.messages.pull.option.status: تضمين حالات واتساب أثناء سحب السجل.
cli.cmd.messages.pull.option.broadcast: تضمين قوائم البث أثناء سحب السجل.
cli.cmd.messages.pull.option.media:
تنزيل المرفقات (صور، فيديوهات، ملفات) أثناء سحب السجل.
cli.cmd.messages.pull.option.pause:
وضع مهمة سحب الرسائل في قائمة الانتظار وهي متوقفة مؤقتًا وإيقاف معالجة رسائل
واتساب الجديدة حتى تكتمل.
cli.cmd.messages.pull.option.resolve-conversations:
وضع علامة على المحادثات كمحلولة بعد معالجة كل رسائل السجل.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ لا يمكن استخدام `--pause` لأن أمر **queue** معطَّل في هذا التكوين.
cli.cmd.queue.description: إدارة قوائم الانتظار.
cli.cmd.queue.summary: تنفيذ إجراءات على قوائم الانتظار.
cli.cmd.queue.action.description: اختر إجراءً.
cli.cmd.queue.status.description: عرض حالة قائمة الانتظار.
cli.cmd.queue.argument.name:
'اسم قائمة الانتظار: `inbox` أو `whatsapp` (اختياري).'
cli.cmd.queue.start.description: بدء قائمة الانتظار بالاسم.
cli.cmd.queue.stop.description: إيقاف قائمة الانتظار بالاسم.
cli.cmd.queue.paused:
⚪🧩 تم إيقاف قوائم الانتظار مؤقتًا. أرسل `queue status` للتحقق من الحالة.
cli.cmd.queue.resumed:
🟢🧩 تم استئناف قوائم الانتظار. أرسل `queue status` للتحقق من الحالة.
cli.cmd.queue.status.result: >-
📊 حالة قائمة الانتظار:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ متوقفة
مؤقتًا{{/paused}}{{^paused}}🟢 تعمل{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
تحديد مدة محاولة تنزيل الوسائط لكل رسالة.
cli.cmd.messages.pull.argument.end:
تحديد نقطة نهاية نافذة السحب؛ على سبيل المثال، `2d` يعني التوقف قبل يومين.
cli.cmd.messages.pull.argument.start:
تحديد نقطة بداية نافذة السحب؛ على سبيل المثال، `1d` يبدأ قبل يوم واحد (القيمة
الافتراضية `0d` أي الآن).
cli.cmd.messages.pull.already-running: >-
⏳ مهمة **Pull Messages** تعمل بالفعل لـ **{{{chat}}}**. انتظر حتى تنتهي أو
أوقفها قبل بدء مهمة جديدة.
أرسل `messages status` للحصول على التفاصيل.
cli.cmd.messages.status.not-found:
ℹ️ لم يتم العثور على مهمة **Pull Messages** نشطة. ابدأ واحدة باستخدام
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
لم يتم العثور على دردشات واتساب للسحب خلال هذه الفترة.
cli.cmd.contacts.pull.option.progress:
الإبلاغ عن التقدم كل عدد N من جهات الاتصال. القيمة 0 لتعطيل التقارير.
cli.cmd.contacts.pull.option.lids:
تضمين جهات اتصال واتساب LID (مجهولة) في المزامنة.
task.contacts.completed:
👤✅ تم إنهاء جهات الاتصال → 🆕 {{progress.created}}، 🔄 {{progress.updated}}،
⚪ {{progress.skipped}}، 🔴 {{progress.errors}}، 🖼️
{{progress.avatar.updated}}
task.messages.started: ⏳ جارٍ سحب الرسائل 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
أرسل "{{{prefix}}}messages status" للاطلاع على المزيد من التفاصيل.
task.messages.progress:
'⏳ رسائل {{{chat}}}: 🆕 {{progress.ok}}، 🟢 {{progress.exists}}، ⚪
{{progress.ignored}}، 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ رسائل {{{chat}}} خلال {{{period}}} → 🆕 {{progress.ok}}، 🟢
{{progress.exists}}، ⚪ {{progress.ignored}}، 🔴 {{progress.errors}}
task.messages.status: |-
رسائل **{{{chat}}}** خلال الفترة ⏱️ **{{{period}}}**. ⚙️ حالة المهمة: **{{state}}**.
🗨️ الرسائل:
- 🆕 تم الإنشاء: **{{{progress.ok}}}**
- 🟢 موجودة مسبقًا: {{{progress.exists}}}
- ⚪ تم تجاهلها: {{{progress.ignored}}}
- 🔴 أخطاء: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ الأقدم: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ الأحدث: **{{{messages.last}}}**
{{/messages.last}}
💬 دردشات واتساب:
- 🆕 رسائل جديدة في **{{{chats}}}** دردشات
{{#options.resolveConversations}}
🗣️ المحادثات:
- 🟢 تم حلها: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 أخطاء: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
التفاصيل:
[{{{details.text}}}]({{{details.url}}})
+345 -37
View File
@@ -1,4 +1,4 @@
# Bengali translations for ChatWoot integration
# Bengali translations for WhatsApp inbox integration
locale.name: '🇧🇩 বাংলা (Bengali)'
# বেস লোকেল কোড - এটি একটি বৈধ BCP 47 লোকেল হতে হবে
@@ -6,23 +6,28 @@ locale.name: '🇧🇩 বাংলা (Bengali)'
# WAHA এটি সংখ্যা, মুদ্রা এবং তারিখ/সময়ের ফরম্যাটিংয়ে ব্যবহার করে।
locale.base: 'bn-BD'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন (WAHA)'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString এর "options"
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: bn-BD
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: true
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Dhaka'
Today: 'আজ'
Yesterday: 'গতকাল'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: হোয়াটসঅ্যাপ থেক
job.scheduled.error.header: নির্ধারিত কাজ সম্পাদন করতে ব্যর্থ হয়েছে
message.from.whatsapp: |-
{{{text}}}
*📱 হোয়াটসঅ্যাপ থেকে পাঠানো হয়েছে*
📱 *হোয়াটসঅ্যাপ থেকে পাঠানো হয়েছে*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 API এর মাধ্যমে পাঠানো হয়েছে*
📱 *API এর মাধ্যমে পাঠানো হয়েছে*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *এই বার্তাটি হোয়াটসঅ্যাপে মুছে ফেলা হয়েছে*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'প্রচার তালিকা'
whatsapp.contact.status.name: 'স্ট্যাটাস'
app.connected.message: |-
🔗 **সংযুক্ত: হোয়াটসঅ্যাপ সেশন থেকে ChatWoot ইনবক্স**
🔗 **সংযুক্ত: হোয়াটসঅ্যাপ সেশন থেকে ইনবক্স**
✅ হোয়াটসঅ্যাপ সেশন **{{{name}}}** সফলভাবে এই ChatWoot ইনবক্সের সাথে সংযুক্ত করা হয়েছে।
✅ হোয়াটসঅ্যাপ সেশন **{{{name}}}** সফলভাবে এই ইনবক্সের সাথে সংযুক্ত করা হয়েছে।
এই ইনবক্সটি এখন আপনার **হোয়াটসঅ্যাপ অ্যাকাউন্টের** সাথে সংযুক্ত। আপনি এখান থেকে সরাসরি বার্তা পাঠাতে এবং গ্রহণ করতে পারেন — ঠিক হোয়াটসঅ্যাপ ব্যবহার করার মতো, কিন্তু ChatWoot এর ভিতরে।
এই ইনবক্সটি এখন আপনার **হোয়াটসঅ্যাপ অ্যাকাউন্টের** সাথে সংযুক্ত। আপনি এখান থেকে সরাসরি বার্তা পাঠাতে এবং গ্রহণ করতে পারেন — ঠিক হোয়াটসঅ্যাপ ব্যবহার করার মতো, কিন্তু এই ওয়ার্কস্পেসের ভিতরে।
আপনি এই ইনবক্সের সাথে সংযুক্ত হোয়াটসঅ্যাপ সেশনও পরিচালনা করতে পারেন। উপলব্ধ কার্যগুলি দেখতে, এই চ্যাটে **help** পাঠান।
@@ -104,9 +109,9 @@ app.connected.message: |-
চলুন শুরু করা যাক! 🚀
app.disconnected.message: |-
🔌 **সংযোগ বিচ্ছিন্ন: হোয়াটসঅ্যাপ সেশন থেকে ChatWoot ইনবক্স**
🔌 **সংযোগ বিচ্ছিন্ন: হোয়াটসঅ্যাপ সেশন থেকে ইনবক্স**
❌ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এই ChatWoot ইনবক্স থেকে সংযোগ বিচ্ছিন্ন করা হয়েছে।
❌ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এই ইনবক্স থেকে সংযোগ বিচ্ছিন্ন করা হয়েছে।
আপনি আর হোয়াটসঅ্যাপ থেকে বার্তা পাঠাতে বা গ্রহণ করতে পারবেন না।
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এর সেটিংস আপডেট করা হয়েছে।
app.commands.list: |-
আপনি নিম্নলিখিত কমান্ডগুলির একটি পাঠিয়ে **হোয়াটসঅ্যাপ ইন্টিগ্রেশন** পরিচালনা করতে পারেন:
cli.usage.arguments: |-
[অ্যাকশন]
cli.usage.command: |-
[কমান্ড]
cli.usage.options: |-
[বিকল্প]
cli.cmd.disabled: ⛔ **`{{command}}` কমান্ডটি নিষ্ক্রিয় করা হয়েছে**।
অনুগ্রহ করে আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।
উপলব্ধ কমান্ডগুলির তালিকা পেতে **help** পাঠান।
cli.cmd.root.description: |-
আপনার **হোয়াটসঅ্যাপ ইন্টিগ্রেশন** পরিচালনা করুন!
cli.cmd.root.sub.session: |-
🖥️ **সেশন**:
1. **status** - বর্তমান সেশনের স্ট্যাটাস পান।
2. **restart** - সেশন পুনরায় চালু করুন - এটি আপনার হোয়াটসঅ্যাপ অ্যাকাউন্ট পুনরায় সংযোগ করার চেষ্টা করবে।
3. **start** - সেশন শুরু করুন।
4. **stop** - সেশন বন্ধ করুন।
5. **logout** - সেশন থেকে লগআউট করুন, যাতে আপনি QR কোড স্ক্যান করতে পারেন এবং সেশন মেরামত করতে পারেন।
6. **qr** - সেশনের জন্য QR কোড পান।
7. **screenshot** - সেশনের স্ক্রিনশট পান (শুধুমাত্র **WEBJS** ইঞ্জিনে উপলব্ধ)।
8. **help** - উপলব্ধ কমান্ডগুলি।
{{#commands.server}}
⚙️ সার্ভার:
- **server status** - সার্ভারের সংস্করণ এবং স্ট্যাটাস তথ্য পান।
- **server reboot** - সার্ভার সুন্দরভাবে পুনরায় চালু করুন।
- **server reboot force** - সার্ভার জোর করে পুনরায় চালু করুন।
{{/commands.server}}
cli.cmd.root.sub.server: |-
🖥️ **সার্ভার**:
এই কথোপকথনে একটি **কমান্ড** (কেস সংবেদনশীল নয়) বা **কমান্ড নম্বর** পাঠান।
cli.cmd.root.sub.sync: |-
🔄️ **সিঙ্ক**:
app.commands.server.disabled: ⛔ **সার্ভার কমান্ড নিষ্ক্রিয় করা হয়েছে**।
cli.cmd.session.status.description: |-
বর্তমান সেশনের স্ট্যাটাস দেখুন।
দয়া করে আপনার প্রশাসকের সাথে যোগাযোগ করুন।
cli.cmd.session.restart.description: |-
সেশন পুনরায় চালু করুন।
উপলব্ধ কমান্ডের তালিকা পেতে **help** পাঠান।
cli.cmd.session.start.description: |-
সেশন শুরু করুন।
cli.cmd.session.stop.description: |-
সেশন বন্ধ করুন।
cli.cmd.session.logout.description: |-
সংযোগ পুনরুদ্ধার করতে নতুন QR কোড স্ক্যান করার জন্য সেশন থেকে লগ আউট করুন।
cli.cmd.session.qr.description: |-
সেশনের QR কোড দেখান।
cli.cmd.session.screenshot.description: |-
সেশনের স্ক্রিনশট নিন (শুধুমাত্র **WEBJS** ইঞ্জিনে উপলব্ধ)।
cli.cmd.contacts.summary: |-
হোয়াটসঅ্যাপ কনট্যাক্টস আনুন।
cli.cmd.contacts.description: |-
👤 সিঙ্ক শুরু করতে `contacts pull` চালান বা অগ্রগতি দেখতে `contacts status` চালান।
cli.cmd.contacts.action.description: |-
`start` দিয়ে কনট্যাক্ট টানা শুরু করুন বা `status` দিয়ে সক্রিয় কাজ পরীক্ষা করুন।
cli.cmd.contacts.pull.description: |-
হোয়াটসঅ্যাপ কনট্যাক্টস ইনবক্সে আনা শুরু করুন।
cli.cmd.contacts.pull.option.batch: |-
প্রতি ব্যাচে কত কনট্যাক্ট টানা হবে।
cli.cmd.contacts.pull.option.delay-contact: |-
প্রতিটি কনট্যাক্ট সিঙ্কের মাঝে বিলম্ব, যাতে অ্যাভাটার টানার সময় রেট লিমিট এড়ানো যায়।
cli.cmd.contacts.pull.option.delay-batch: |-
প্রতিটি ব্যাচের মাঝে বিলম্ব।
cli.cmd.contacts.pull.option.avatar: |-
কনট্যাক্টের অ্যাভাটার কীভাবে টানবেন নির্ধারণ করুন: `skip`, `if-missing`, অথবা `update`।
cli.cmd.contacts.pull.option.groups: |-
সিঙ্কে WhatsApp গ্রুপ কনট্যাক্ট অন্তর্ভুক্ত করুন।
cli.cmd.contacts.pull.option.no-attributes: |-
কনট্যাক্ট আগেই থাকলে তার অ্যাট্রিবিউট টানবেন না (এতে প্রক্রিয়া দ্রুত হয়)।
cli.cmd.contacts.pull.already-running: |-
⏳ **Pull Contacts** কাজটি ইতিমধ্যেই চলছে। এটি শেষ হওয়া পর্যন্ত অপেক্ষা করুন বা নতুন কাজ শুরু করার আগে থামান।
বিস্তারিত জানতে `contacts status` পাঠান।
cli.cmd.options.job.attempts: |-
কোনো কাজ ব্যর্থ ঘোষণা করার আগে কয়বার পুনরায় চেষ্টা করা হবে।
cli.cmd.options.job.timeout: |-
কাজ পুনরায় চেষ্টা করার আগে সর্বোচ্চ চালনার সময় (যেমন `30s`, `5m`, `1h`)।
cli.cmd.contacts.status.description: |-
চলমান টানার কাজের অবস্থা দেখান।
cli.cmd.contacts.status.not-found: |-
ℹ️ কোনো সক্রিয় **Pull Contacts** কাজ পাওয়া যায়নি। `contacts pull` দিয়ে একটি শুরু করুন।
cli.cmd.server.description: |-
সার্ভার পরিচালনা করুন।
cli.cmd.server.status.description: |-
সার্ভারের সংস্করণ এবং স্ট্যাটাস দেখান।
cli.cmd.server.reboot.description: |-
সার্ভার পুনরায় চালু করুন।
cli.cmd.server.reboot.option.force: |-
অবিলম্বে রিবুট করতে বাধ্য করুন।
cli.help.usage.title: |-
ব্যবহার: কথোপকথনে নীচের ফরম্যাটে একটি কমান্ড পাঠান:
cli.help.commands.defaultGroup: |-
**কমান্ডসমূহ:**
cli.help.arguments.title: |-
**আর্গুমেন্টসমূহ:**
cli.help.options.defaultGroup: |-
**বিকল্পসমূহ:**
cli.help.globalOptions.title: |-
**গ্লোবাল বিকল্পসমূহ:**
task.contacts.status: |-
👤 কনট্যাক্ট টানা: **{{state}}**
- ✅ সফল: {{{progress.ok}}} কনট্যাক্ট
- ⚪ এড়িয়ে গেছে: {{{progress.skipped}}} কনট্যাক্ট
- ⚠️ ত্রুটি: {{{progress.errors}}} কনট্যাক্ট
{{#job}}
⌛ সময়রেখা:
- কিউ হয়েছে: {{{timestamp}}}
{{#processedOn}}
- শুরু হয়েছে: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- শেষ হয়েছে: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
বিস্তারিত:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
কনট্যাক্ট টানা হচ্ছে...
task.contacts.progress: |-
কনট্যাক্ট টানা: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
উপলব্ধ কমান্ডগুলির তালিকা পেতে **help** পাঠান।
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ এই ChatWoot ইনবক্স **হোয়াটসঅ্যাপ অ্যাকাউন্টের** সাথে সংযুক্ত এবং বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত!
✅ এই ইনবক্সটি আপনার **হোয়াটসঅ্যাপ অ্যাকাউন্টের** সাথে সংযুক্ত এবং বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **এই ChatWoot ইনবক্স হোয়াটসঅ্যাপ অ্যাকাউন্টের সাথে সংযুক্ত কিন্তু বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত নয়!**
⚠️ **এই ইনবক্সটি হোয়াটসঅ্যাপ অ্যাকাউন্টের সাথে সংযুক্ত কিন্তু বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত নয়!**
অনুগ্রহ করে সেশনের স্ট্যাটাস পরীক্ষা করুন এবং এটি পুনরায় চালু করার চেষ্টা করুন অথবা লগআউট করুন এবং QR কোড আবার স্ক্যান করুন।
app.session.scan.qr.code: |-
⚠️ **এই ChatWoot ইনবক্সের সাথে হোয়াটসঅ্যাপ অ্যাকাউন্ট লিঙ্ক করতে আপনার ফোনে QR কোড স্ক্যান করুন!**
⚠️ **এই ইনবক্সের সাথে হোয়াটসঅ্যাপ অ্যাকাউন্ট লিঙ্ক করতে আপনার ফোনে QR কোড স্ক্যান করুন!**
app.server.version.and.status: |-
**সংস্করণ:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **এই ধরনের বার্তা ChatWoot-এ সমর্থিত নয়।**
⚠️ **এই ধরনের বার্তা এই ইনবক্সে সমর্থিত নয়।**
📱 এটি দেখতে অনুগ্রহ করে **WhatsApp** খুলুন।
বিস্তারিত: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **অ্যালবাম বার্তা প্রাপ্ত**
{{#totalCount}}
**{{totalCount}}** মিডিয়া ফাইল সহ একটি অ্যালবাম প্রাপ্ত হয়েছে।
{{#expectedImageCount}}
📸 ছবি: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 ভিডিও: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
মিডিয়া ফাইলগুলি পরবর্তী বার্তাগুলিতে সরবরাহ করা হবে।
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ফেসবুক বিজ্ঞাপন**
@@ -352,3 +522,141 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **গ্রাহকের বার্তা:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: অজানা
ERROR: ত্রুটি
PENDING: পাঠানো হচ্ছে
SERVER: সার্ভার
DEVICE: ডিভাইস
READ: পড়া হয়েছে
PLAYED: চালানো হয়েছে
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **বার্তায় মিডিয়া আছে, কিন্তু আমরা এটি ডাউনলোড করতে পারিনি**
📱 দেখার জন্য অনুগ্রহ করে **WhatsApp** খুলুন।
{{^content}}
বিস্তারিত: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **কিউ**:'
cli.cmd.messages.summary: WhatsApp বার্তা টানুন।
cli.cmd.messages.description:
💬 বার্তা সিঙ্ক শুরু করতে `messages pull` চালান অথবা অগ্রগতি দেখতে `messages
status` চালান।
cli.cmd.messages.action.description:
বার্তা টানতে `pull` ব্যবহার করুন অথবা সক্রিয় কাজ দেখার জন্য `status`।
cli.cmd.messages.pull.option.chat:
নির্দিষ্ট WhatsApp চ্যাট আইডিতে টান সীমাবদ্ধ করুন।
cli.cmd.messages.pull.option.force:
WhatsApp বার্তা কথোপকথনের ম্যাপিংয়ে থাকলেও ইনবক্সকে বার্তা তৈরি করতে বাধ্য
করুন।
cli.cmd.messages.pull.option.batch: প্রতিটি ব্যাচে কতটি বার্তা টানবেন।
cli.cmd.messages.pull.option.progress:
প্রতি N বার্তায় অগ্রগতি জানায়। রিপোর্ট বন্ধ করতে 0 সেট করুন।
cli.cmd.messages.pull.option.no-dm: ইতিহাস টানার সময় সরাসরি বার্তা উপেক্ষা করুন।
cli.cmd.messages.pull.option.groups:
ইতিহাস টানার সময় WhatsApp গ্রুপ অন্তর্ভুক্ত করুন।
cli.cmd.messages.pull.option.channels:
ইতিহাস টানার সময় WhatsApp চ্যানেল অন্তর্ভুক্ত করুন।
cli.cmd.messages.pull.option.status:
ইতিহাস টানার সময় WhatsApp স্ট্যাটাস আপডেট অন্তর্ভুক্ত করুন।
cli.cmd.messages.pull.option.broadcast:
ইতিহাস টানার সময় ব্রডকাস্ট তালিকা অন্তর্ভুক্ত করুন।
cli.cmd.messages.pull.option.media:
ইতিহাস টানার সময় মিডিয়া (ছবি, ভিডিও, ফাইল) ডাউনলোড করুন।
cli.cmd.messages.pull.option.pause:
বার্তা টানার কাজটিকে বিরতি অবস্থায় কিউতে রাখে এবং শেষ না হওয়া পর্যন্ত নতুন
WhatsApp বার্তার প্রসেসিং বন্ধ রাখে।
cli.cmd.messages.pull.option.resolve-conversations:
সমস্ত ইতিহাস বার্তা প্রক্রিয়ার পর কথোপকথনগুলোকে সমাধান হয়েছে হিসেবে চিহ্নিত
করে।
cli.cmd.messages.pull.error.pause-no-queue:
⛔ `--pause` ব্যবহার করা যাবে না কারণ এই কনফিগারেশনে **queue** কমান্ড
নিষ্ক্রিয়।
cli.cmd.queue.description: কিউ পরিচালনা করুন।
cli.cmd.queue.summary: কিউ-তে কর্ম সম্পাদন করুন।
cli.cmd.queue.action.description: একটি কর্ম নির্বাচন করুন।
cli.cmd.queue.status.description: কিউ-র অবস্থা দেখুন।
cli.cmd.queue.argument.name: 'কিউ নাম: `inbox` বা `whatsapp` (ঐচ্ছিক)।'
cli.cmd.queue.start.description: নামের ভিত্তিতে কিউ শুরু করুন।
cli.cmd.queue.stop.description: নামের ভিত্তিতে কিউ বন্ধ করুন।
cli.cmd.queue.paused: ⚪🧩 কিউ বিরতিতে আছে। অবস্থা দেখতে `queue status` পাঠান।
cli.cmd.queue.resumed:
🟢🧩 কিউ আবার চালু হয়েছে। অবস্থা দেখতে `queue status` পাঠান।
cli.cmd.queue.status.result: >-
📊 কিউ অবস্থা:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
বিরতিতে{{/paused}}{{^paused}}🟢 চলমান{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
প্রতিটি বার্তার জন্য মিডিয়া ডাউনলোডের সময়সীমা নির্ধারণ করুন।
cli.cmd.messages.pull.argument.end:
টানার উইন্ডো কোথায় শেষ হবে নির্ধারণ করুন; যেমন `2d` দুই দিন আগে থামে।
cli.cmd.messages.pull.argument.start:
টানার উইন্ডো কোথা থেকে শুরু হবে নির্ধারণ করুন; যেমন `1d` এক দিন আগে শুরু হয়
(ডিফল্ট `0d`, অর্থাৎ এখন)।
cli.cmd.messages.pull.already-running: >-
⏳ **Pull Messages** কাজটি ইতিমধ্যে **{{{chat}}}**-এর জন্য চলছে। শেষ হওয়া
পর্যন্ত অপেক্ষা করুন অথবা নতুন শুরু করার আগে এটি বন্ধ করুন।
বিস্তারিত জানার জন্য `messages status` পাঠান।
cli.cmd.messages.status.not-found:
ℹ️ কোনো সক্রিয় **Pull Messages** কাজ পাওয়া যায়নি। `messages pull` দিয়ে একটি
শুরু করুন।
cli.cmd.messages.pull.no-chats-found:
এই সময়সীমার জন্য কোনো WhatsApp চ্যাট পাওয়া যায়নি।
cli.cmd.contacts.pull.option.progress:
প্রতি N কন্টাক্টে অগ্রগতি জানান। রিপোর্ট বন্ধ করতে 0 সেট করুন।
cli.cmd.contacts.pull.option.lids:
সিঙ্কে WhatsApp LID (অনামা) কন্টাক্ট অন্তর্ভুক্ত করুন।
task.contacts.completed:
👤✅ কন্টাক্ট সম্পন্ন → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ বার্তা টানা হচ্ছে 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details: আরও জানতে "{{{prefix}}}messages status" পাঠান।
task.messages.progress:
'⏳ বার্তা {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ বার্তা {{{chat}}} {{{period}}}-এ → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
**{{{chat}}}**-এর বার্তা ⏱️ **{{{period}}}** সময়ে। ⚙️ কাজের অবস্থা: **{{state}}**.
🗨️ বার্তা:
- 🆕 তৈরি: **{{{progress.ok}}}**
- 🟢 আগেই ছিল: {{{progress.exists}}}
- ⚪ উপেক্ষা: {{{progress.ignored}}}
- 🔴 ত্রুটি: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ প্রাচীনতম: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ সর্বশেষ: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp চ্যাট:
- 🆕 **{{{chats}}}** চ্যাটে নতুন বার্তা
{{#options.resolveConversations}}
🗣️ কথোপকথন:
- 🟢 সমাধান হয়েছে: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 ত্রুটি: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
বিস্তারিত:
[{{{details.text}}}]({{{details.url}}})
+364 -37
View File
@@ -1,4 +1,4 @@
# German translations for ChatWoot integration
# German translations for WhatsApp inbox integration
locale.name: '🇩🇪 Deutsch (German)'
# Basis-Gebietsschema-Code - muss eine gültige BCP 47 Locale sein
@@ -6,23 +6,28 @@ locale.name: '🇩🇪 Deutsch (German)'
# WAHA nutzt ihn zur Darstellung von Zahlen, Währungen sowie Datum und Uhrzeit
locale.base: 'de-DE'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.name: 'WhatsApp Integration'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "Optionen" von date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: de-DE
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Berlin'
Today: 'Heute'
Yesterday: 'Gestern'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -54,14 +59,14 @@ whatsapp.message.removing.error:
job.scheduled.error.header: Geplante Aufgabe konnte nicht ausgeführt werden
message.from.whatsapp: |-
{{{text}}}
*📱 Von WhatsApp gesendet*
📱 *Von WhatsApp gesendet*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Über API gesendet*
📱 *Über API gesendet*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Diese Nachricht wurde in WhatsApp gelöscht*
@@ -89,11 +94,11 @@ whatsapp.contact.broadcast.suffix: 'Broadcast-Liste'
whatsapp.contact.status.name: 'Status'
app.connected.message: |-
🔗 **VERBUNDEN: WhatsApp-Sitzung mit ChatWoot Inbox**
🔗 **VERBUNDEN: WhatsApp-Sitzung mit der Inbox**
✅ WhatsApp-Sitzung **{{{name}}}** wurde erfolgreich mit dieser ChatWoot Inbox verbunden.
✅ WhatsApp-Sitzung **{{{name}}}** wurde erfolgreich mit dieser Inbox verbunden.
Diese Inbox ist jetzt mit Ihrem **WhatsApp-Konto** verknüpft. Sie können Nachrichten direkt von hier senden und empfangen — genau wie bei WhatsApp, aber innerhalb von ChatWoot.
Diese Inbox ist jetzt mit Ihrem **WhatsApp-Konto** verknüpft. Sie können Nachrichten direkt von hier senden und empfangen — genau wie bei WhatsApp, aber innerhalb Ihrer Arbeitsumgebung.
Sie können auch die mit dieser Inbox verknüpfte WhatsApp-Sitzung verwalten. Um verfügbare Aktionen anzuzeigen, senden Sie **help** an diesen Chat.
@@ -107,9 +112,9 @@ app.connected.message: |-
Lassen Sie uns beginnen! 🚀
app.disconnected.message: |-
🔌 **GETRENNT: WhatsApp-Sitzung von ChatWoot Inbox**
🔌 **GETRENNT: WhatsApp-Sitzung von der Inbox**
❌ WhatsApp-Sitzung **{{{name}}}** wurde von dieser ChatWoot Inbox getrennt.
❌ WhatsApp-Sitzung **{{{name}}}** wurde von dieser Inbox getrennt.
Sie können keine Nachrichten mehr von WhatsApp senden oder empfangen.
@@ -120,34 +125,156 @@ app.updated.message: |-
✅ Einstellungen für WhatsApp-Sitzung **{{{name}}}** wurden aktualisiert.
app.commands.list: |-
Sie können die **WhatsApp Integration** verwalten, indem Sie einen der folgenden Befehle senden:
cli.usage.arguments: |-
[Aktion]
🖥️ **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.
4. **stop** - die Sitzung stoppen.
5. **logout** - von der Sitzung abmelden, damit Sie den QR-Code scannen und die Sitzung reparieren können.
6. **qr** - den QR-Code für die Sitzung abrufen.
7. **screenshot** - einen Screenshot der Sitzung erhalten (nur in der **WEBJS**-Engine verfügbar).
8. **help** - verfügbare Befehle.
cli.usage.command: |-
[Befehl]
{{#commands.server}}
⚙️ Server:
- **server status** - Serverversion und Statusinformationen abrufen.
- **server reboot** - Server ordnungsgemäß neu starten.
- **server reboot force** - Server erzwungen neu starten.
{{/commands.server}}
cli.usage.options: |-
[Optionen]
Senden Sie einen **Befehl** (Groß-/Kleinschreibung wird nicht beachtet) oder eine **Befehlsnummer** an diese Konversation.
app.commands.server.disabled: ⛔ **Serverbefehle sind deaktiviert**.
cli.cmd.disabled: ⛔ **Der Befehl `{{command}}` ist deaktiviert**.
Bitte wenden Sie sich an Ihren Administrator.
Senden Sie **help**, um die Liste der verfügbaren Befehle zu erhalten.
cli.cmd.root.description: |-
Verwalten Sie Ihre **WhatsApp-Integration**!
cli.cmd.root.sub.session: |-
🖥️ **Sitzung**:
cli.cmd.root.sub.server: |-
🖥️ **Server**:
cli.cmd.root.sub.sync: |-
🔄️ **Synchronisierung**:
cli.cmd.session.status.description: |-
Aktuellen Sitzungsstatus abrufen.
cli.cmd.session.restart.description: |-
Sitzung neu starten.
cli.cmd.session.start.description: |-
Sitzung starten.
cli.cmd.session.stop.description: |-
Sitzung stoppen.
cli.cmd.session.logout.description: |-
Melden Sie die Sitzung ab, um einen neuen QR-Code zu scannen und die Verbindung wiederherzustellen.
cli.cmd.session.qr.description: |-
QR-Code der Sitzung anzeigen.
cli.cmd.session.screenshot.description: |-
Einen Screenshot der Sitzung aufnehmen (nur mit der **WEBJS**-Engine verfügbar).
cli.cmd.contacts.summary: |-
WhatsApp-Kontakte abrufen.
cli.cmd.contacts.description: |-
👤 Führen Sie `contacts pull` aus, um eine Synchronisierung zu starten, oder `contacts status`, um den Fortschritt zu prüfen.
cli.cmd.contacts.action.description: |-
Mit `start` den Abruf beginnen oder mit `status` den aktiven Auftrag prüfen.
cli.cmd.contacts.pull.description: |-
Beginnen Sie, WhatsApp-Kontakte in die Inbox zu importieren.
cli.cmd.contacts.pull.option.batch: |-
Anzahl der Kontakte pro Batch.
cli.cmd.contacts.pull.option.delay-contact: |-
Verzögerung zwischen einzelnen Kontakten, um beim Laden von Avataren Rate Limits zu vermeiden.
cli.cmd.contacts.pull.option.delay-batch: |-
Verzögerung zwischen Kontakt-Batches.
cli.cmd.contacts.pull.option.avatar: |-
Legen Sie fest, wie Avatare geladen werden: `skip`, `if-missing` oder `update`.
cli.cmd.contacts.pull.option.groups: |-
WhatsApp-Gruppenkontakte in die Synchronisierung einbeziehen.
cli.cmd.contacts.pull.option.no-attributes: |-
Keine Kontaktattribute laden, wenn der Kontakt bereits existiert (beschleunigt den Vorgang).
cli.cmd.contacts.pull.already-running: |-
⏳ Der Auftrag **Pull Contacts** läuft bereits. Warten Sie, bis er abgeschlossen ist, oder stoppen Sie ihn, bevor Sie einen neuen starten.
Senden Sie `contacts status` für Details.
cli.cmd.options.job.attempts: |-
Anzahl der erneuten Versuche, bevor der Auftrag als fehlgeschlagen markiert wird.
cli.cmd.options.job.timeout: |-
Maximale Laufzeit, bevor der Auftrag erneut versucht wird (unterstützt Werte wie `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Status des aktuellen Abrufauftrags anzeigen.
cli.cmd.contacts.status.not-found: |-
ℹ️ Kein aktiver **Pull Contacts** Auftrag gefunden. Starten Sie einen mit `contacts pull`.
cli.cmd.server.description: |-
Server verwalten.
cli.cmd.server.status.description: |-
Serverversion und -status anzeigen.
cli.cmd.server.reboot.description: |-
Server neu starten.
cli.cmd.server.reboot.option.force: |-
Neustart sofort erzwingen.
cli.help.usage.title: |-
Verwendung: Senden Sie einen Befehl im folgenden Format an die Konversation:
cli.help.commands.defaultGroup: |-
**Befehle:**
cli.help.arguments.title: |-
**Argumente:**
cli.help.options.defaultGroup: |-
**Optionen:**
cli.help.globalOptions.title: |-
**Globale Optionen:**
task.contacts.status: |-
👤 Kontakte abrufen: **{{state}}**
- ✅ Erfolgreich: {{{progress.ok}}} Kontakte
- ⚪ Übersprungen: {{{progress.skipped}}} Kontakte
- ⚠️ Fehler: {{{progress.errors}}} Kontakte
{{#job}}
⌛ Zeitplan:
- Eingereiht: {{{timestamp}}}
{{#processedOn}}
- Start: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Ende: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Details:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Kontakte werden abgerufen...
task.contacts.progress: |-
Kontakte abrufen: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Senden Sie **help**, um die Liste der verfügbaren Befehle zu erhalten.
@@ -161,17 +288,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Diese ChatWoot Inbox ist mit dem **WhatsApp-Konto** verknüpft und bereit zum Senden und Empfangen von Nachrichten!
✅ Diese Inbox ist mit Ihrem **WhatsApp-Konto** verknüpft und bereit zum Senden und Empfangen von Nachrichten!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **Diese ChatWoot Inbox ist mit dem WhatsApp-Konto verknüpft, aber NICHT bereit zum Senden und Empfangen von Nachrichten!**
⚠️ **Diese 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: |-
⚠️ **Scannen Sie den QR-Code auf Ihrem Telefon, um das WhatsApp-Konto mit dieser ChatWoot Inbox zu verknüpfen!**
⚠️ **Scannen Sie den QR-Code auf Ihrem Telefon, um das WhatsApp-Konto mit dieser Inbox zu verknüpfen!**
app.server.version.and.status: |-
**Version:**
@@ -286,6 +413,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -336,11 +491,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Dieser Nachrichtentyp wird in ChatWoot nicht unterstützt.**
⚠️ **Dieser Nachrichtentyp wird in dieser Inbox nicht unterstützt.**
📱 Bitte öffne **WhatsApp**, um ihn anzusehen.
Details: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Album-Nachricht Empfangen**
{{#totalCount}}
Ein Album mit **{{totalCount}}** Mediendatei(en) erhalten.
{{#expectedImageCount}}
📸 Bilder: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Videos: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Die Mediendateien werden in den folgenden Nachrichten geliefert.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK-ANZEIGE**
@@ -355,3 +525,160 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Kundennachricht:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Unbekannt
ERROR: Fehler
PENDING: Wird gesendet
SERVER: Server
DEVICE: Gerät
READ: Gelesen
PLAYED: Abgespielt
whatsapp.to.chatwoot.message.has.media.no.media: >-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **Diese Nachricht enthält Medien, konnte aber nicht heruntergeladen
werden**
📱 Bitte öffnen Sie **WhatsApp**, um sie anzusehen.
{{^content}}
Details: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Warteschlange**:'
cli.cmd.messages.summary: WhatsApp-Nachrichten abrufen.
cli.cmd.messages.description:
💬 Führen Sie `messages pull` aus, um eine Synchronisierung zu starten, oder
`messages status`, um den Fortschritt zu prüfen.
cli.cmd.messages.action.description:
'`pull`, um das Abrufen zu starten, oder `status`, um den aktuellen Job zu
prüfen.'
cli.cmd.messages.pull.option.chat:
Beschränkt das Abrufen auf eine bestimmte WhatsApp-Chat-ID.
cli.cmd.messages.pull.option.force:
Erzwingt, dass die Inbox Nachrichten erstellt, auch wenn die
WhatsApp-Nachricht bereits im Gesprächs-Mapping vorhanden ist.
cli.cmd.messages.pull.option.batch:
Anzahl der Nachrichten, die pro Paket abgerufen werden.
cli.cmd.messages.pull.option.progress:
Meldet den Fortschritt nach N Nachrichten. 0 deaktiviert die
Fortschrittsmeldungen.
cli.cmd.messages.pull.option.no-dm:
Ignoriert Direktnachrichten beim Abrufen des Verlaufs.
cli.cmd.messages.pull.option.groups:
Bezieht WhatsApp-Gruppen beim Abrufen mit ein.
cli.cmd.messages.pull.option.channels:
Bezieht WhatsApp-Kanäle beim Abrufen mit ein.
cli.cmd.messages.pull.option.status:
Bezieht WhatsApp-Statusmeldungen beim Abrufen mit ein.
cli.cmd.messages.pull.option.broadcast:
Bezieht Broadcast-Listen beim Abrufen mit ein.
cli.cmd.messages.pull.option.media:
Lädt Medienanhänge (Bilder, Videos, Dateien) beim Abrufen herunter.
cli.cmd.messages.pull.option.pause:
Stellt den Nachrichtenabruf in der Warteschlange auf Pause und stoppt neue
WhatsApp-Verarbeitung bis zum Abschluss.
cli.cmd.messages.pull.option.resolve-conversations:
Markiert die Gespräche nach Abschluss aller historischen Nachrichten als
erledigt.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ `--pause` kann nicht verwendet werden, weil der **queue**-Befehl in dieser
Konfiguration deaktiviert ist.
cli.cmd.queue.description: Warteschlangen verwalten.
cli.cmd.queue.summary: Aktionen für Warteschlangen durchführen.
cli.cmd.queue.action.description: Wählen Sie eine Aktion.
cli.cmd.queue.status.description: Zeigt den Status einer Warteschlange an.
cli.cmd.queue.argument.name:
'Name der Warteschlange: `inbox` oder `whatsapp` (optional).'
cli.cmd.queue.start.description: Startet eine Warteschlange anhand des Namens.
cli.cmd.queue.stop.description: Stoppt eine Warteschlange anhand des Namens.
cli.cmd.queue.paused:
⚪🧩 Die Warteschlangen wurden pausiert. Senden Sie `queue status`, um den
Status zu prüfen.
cli.cmd.queue.resumed:
🟢🧩 Die Warteschlangen wurden fortgesetzt. Senden Sie `queue status`, um den
Status zu prüfen.
cli.cmd.queue.status.result: >-
📊 Warteschlangenstatus:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
Pausiert{{/paused}}{{^paused}}🟢 Aktiv{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Legt fest, wie lange versucht wird, Medien je Nachricht herunterzuladen.
cli.cmd.messages.pull.argument.end:
Legt fest, wo das Abruffenster endet; `2d` bedeutet vor zwei Tagen.
cli.cmd.messages.pull.argument.start:
Legt fest, wo das Abruffenster beginnt; `1d` bedeutet vor einem Tag (Standard
`0d`, also jetzt).
cli.cmd.messages.pull.already-running: >-
⏳ Der Job **Pull Messages** läuft bereits für **{{{chat}}}**. Warten Sie bis
zum Abschluss oder stoppen Sie ihn, bevor Sie einen neuen starten.
Senden Sie `messages status` für Details.
cli.cmd.messages.status.not-found:
ℹ️ Kein aktiver **Pull Messages**-Job gefunden. Starten Sie einen mit
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
Keine WhatsApp-Chats für diesen Zeitraum gefunden.
cli.cmd.contacts.pull.option.progress:
Meldet den Fortschritt nach N Kontakten. 0 deaktiviert die Meldungen.
cli.cmd.contacts.pull.option.lids:
Bezieht WhatsApp-LID-Kontakte (anonyme Kontakte) in die Synchronisierung ein.
task.contacts.completed:
👤✅ Kontakte abgeschlossen → 🆕 {{progress.created}}, 🔄
{{progress.updated}}, ⚪ {{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️
{{progress.avatar.updated}}
task.messages.started:
⏳ Nachrichten werden abgerufen 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Senden Sie "{{{prefix}}}messages status", um weitere Details zu sehen.
task.messages.progress:
'⏳ Nachrichten {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Nachrichten {{{chat}}} in {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Nachrichten **{{{chat}}}** im Zeitraum ⏱️ **{{{period}}}**. ⚙️ Jobstatus: **{{state}}**.
🗨️ Nachrichten:
- 🆕 Erstellt: **{{{progress.ok}}}**
- 🟢 Bereits vorhanden: {{{progress.exists}}}
- ⚪ Ignoriert: {{{progress.ignored}}}
- 🔴 Fehler: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Älteste: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Neueste: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp-Chats:
- 🆕 Neue Nachrichten in **{{{chats}}}** Chats
{{#options.resolveConversations}}
🗣️ Konversationen:
- 🟢 Aufgelöst: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Fehler: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Details:
[{{{details.text}}}]({{{details.url}}})
+388 -37
View File
@@ -1,4 +1,4 @@
# English translations for ChatWoot integration
# English translations for WhatsApp inbox integration
locale.name: '🇺🇸 English (English)'
# Base locale code - must be a valid BCP 47 locale
@@ -6,22 +6,27 @@ locale.name: '🇺🇸 English (English)'
# WAHA uses it for number, currency and datetime representation
locale.base: 'en-US'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.name: 'WhatsApp Integration'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" from date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: true
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'America/New_York'
Today: Today
Yesterday: Yesterday
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -50,14 +55,14 @@ whatsapp.message.removing.error: Couldn't remove a message from WhatsApp
job.scheduled.error.header: Scheduled job failed to execute
message.from.whatsapp: |-
{{{text}}}
*📱 Sent from WhatsApp*
📱 *Sent from WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Sent via API*
📱 *Sent via API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *This message was deleted in WhatsApp*
@@ -72,6 +77,32 @@ whatsapp.group.message: |-
{{{text}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
# WhatsApp Messages Ack
UNKNOWN: |-
Unknown
ERROR: |-
Error
PENDING: |-
Sending
SERVER: |-
Server
DEVICE: |-
Device
READ: |-
Read
PLAYED: |-
Played
whatsapp.reaction.added: |-
*Reacted* {{{emoji}}}
@@ -160,6 +191,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -209,12 +268,39 @@ whatsapp.to.chatwoot.message.pix: |-
💳 **PIX Copy and Paste sent**
{{/pixData}}
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **The message has media, but we couldn't download it**
📱 Please open **WhatsApp** to view it.
{{^content}}
Details: [{{{details.text}}}]({{{details.url}}})
{{/content}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **This message type is not supported in ChatWoot.**
⚠️ **This message type is not supported in this inbox.**
📱 Please open **WhatsApp** to view it.
Details: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Album Message Received**
{{#totalCount}}
Received an album with **{{totalCount}}** media file(s).
{{#expectedImageCount}}
📸 Images: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Videos: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
The media files will be delivered in the following messages.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK AD**
@@ -231,11 +317,11 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
{{{payload.body}}}
app.connected.message: |-
🔗 **CONNECTED: WhatsApp Session to ChatWoot Inbox**
🔗 **CONNECTED: WhatsApp Session to Inbox**
✅ WhatsApp session **{{{name}}}** has been successfully connected to this ChatWoot Inbox.
✅ WhatsApp session **{{{name}}}** has been successfully connected to this inbox.
This inbox is now linked to your **WhatsApp Account**. You can send and receive messages directly from here — just like using WhatsApp, but inside ChatWoot.
This inbox is now linked to your **WhatsApp Account**. You can send and receive messages directly from here — just like using WhatsApp, but inside your workspace.
You can also manage the WhatsApp session linked to this inbox. To view available actions, send **help** to this chat.
@@ -249,9 +335,9 @@ app.connected.message: |-
Let's get started! 🚀
app.disconnected.message: |-
🔌 **DISCONNECTED: WhatsApp Session from ChatWoot Inbox**
🔌 **DISCONNECTED: WhatsApp Session from Inbox**
❌ WhatsApp session **{{{name}}}** has been disconnected from this ChatWoot Inbox.
❌ WhatsApp session **{{{name}}}** has been disconnected from this inbox.
You will no longer be able to send or receive messages from WhatsApp.
@@ -262,34 +348,299 @@ app.updated.message: |-
✅ Settings for WhatsApp session **{{{name}}}** have been updated.
app.commands.list: |-
You can manage **WhatsApp Integration** by sending one of the following commands:
cli.usage.arguments: |-
[action]
🖥️ **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.
4. **stop** - stop the session.
5. **logout** - logout from the session, so you can scan QR code and repair the session.
6. **qr** - get the QR code for the session.
7. **screenshot** - get a screenshot of the session (only available in **WEBJS** engine).
8. **help** - available commands.
cli.usage.command: |-
[command]
{{#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.
cli.usage.options: |-
[options]
{{/commands.server}}
Send a **command** (case insensitive) or a **command number** to this conversation.
app.commands.server.disabled: ⛔ **Server commands are disabled**.
cli.cmd.disabled: ⛔ **`{{command}}` command is disabled**.
Please contact your administrator.
Send **help** to get the list of available commands.
cli.cmd.root.description: |-
Manage your **WhatsApp Integration**!
cli.cmd.root.sub.session: |-
🖥️ **Session**:
cli.cmd.root.sub.queue: |-
🧩 **Queue**:
cli.cmd.root.sub.server: |-
🔧 **Server**:
cli.cmd.root.sub.sync: |-
🔄️ **Sync**:
cli.cmd.session.status.description: |-
Get current session status.
cli.cmd.session.restart.description: |-
Restart the session.
cli.cmd.session.start.description: |-
Start the session.
cli.cmd.session.stop.description: |-
Stop the session.
cli.cmd.session.logout.description: |-
Log out of the session to scan a new QR code and restore the connection.
cli.cmd.session.qr.description: |-
Show the session QR code.
cli.cmd.session.screenshot.description: |-
Capture a session screenshot (available only with the **WEBJS** engine).
cli.cmd.messages.summary: |-
Pull WhatsApp messages.
cli.cmd.messages.description: |-
💬 Run `messages pull` to request message sync or `messages status` to review progress.
cli.cmd.messages.action.description: |-
`pull` to start a message pull or `status` to inspect the active job.
cli.cmd.messages.pull.option.chat: |-
Limit the pull to a specific WhatsApp Chat ID.
cli.cmd.messages.pull.option.force: |-
Force the inbox to create messages even when the WhatsApp message already exists in the conversation mapping.
cli.cmd.messages.pull.option.batch: |-
Number of messages to pull in each batch.
cli.cmd.messages.pull.option.progress: |-
Report progress every N messages. Set to 0 to disable progress reports.
cli.cmd.messages.pull.option.no-dm: |-
Skip direct messages while pulling history.
cli.cmd.messages.pull.option.groups: |-
Include WhatsApp group chats when pulling history.
cli.cmd.messages.pull.option.channels: |-
Include WhatsApp Channels when pulling history.
cli.cmd.messages.pull.option.status: |-
Include WhatsApp status updates when pulling history.
cli.cmd.messages.pull.option.broadcast: |-
Include broadcast lists when pulling history.
cli.cmd.messages.pull.option.media: |-
Fetch media attachments (images, videos, files) when pulling history.
cli.cmd.messages.pull.option.pause: |-
Enqueue the messages pull job in a paused state, halting new WhatsApp message processing until the pull completes.
cli.cmd.messages.pull.option.resolve-conversations: |-
Mark the conversations as resolved after all history messages are processed.
cli.cmd.messages.pull.error.pause-no-queue: |-
⛔ Cannot use `--pause` because the **queue** command is disabled in this app configuration.
cli.cmd.queue.description: |-
Manage queues.
cli.cmd.queue.summary: |-
Perform queue actions.
cli.cmd.queue.action.description: |-
Choose an action.
cli.cmd.queue.status.description: |-
Show the status of a queue.
cli.cmd.queue.argument.name: |-
Queue name: `inbox` or `whatsapp` (optional).
cli.cmd.queue.start.description: |-
Start a queue by name.
cli.cmd.queue.stop.description: |-
Stop a queue by name.
cli.cmd.queue.paused: |-
⚪🧩 Queues has been paused. Send `queue status` to check the status.
cli.cmd.queue.resumed: |-
🟢🧩 Queues has been resumed. Send `queue status` to check the status.
cli.cmd.queue.status.result: |-
📊 Queue Status:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ Paused{{/paused}}{{^paused}}🟢 Running{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media: |-
Set how long to try downloading media for each message.
cli.cmd.messages.pull.argument.end: |-
Set how far back the pull window ends, e.g., `2d` stops at two days ago.
cli.cmd.messages.pull.argument.start: |-
Set where the pull window starts, e.g., `1d` begins one day ago (defaults to `0d`, meaning now).
cli.cmd.messages.pull.already-running: |-
⏳ **Pull Messages** job is already running for **{{{chat}}}**. Wait for it to finish or stop it before starting a new one.
Send `messages status` for details.
cli.cmd.messages.status.not-found: |-
ℹ️ No active **Pull Messages** job found. Start one with `messages pull`.
cli.cmd.messages.pull.no-chats-found: |-
No WhatsApp chats found to pull messages from for the period.
cli.cmd.contacts.summary: |-
Pull WhatsApp contacts.
cli.cmd.contacts.description: |-
👤 Run `contacts pull` to launch a sync or `contacts status` to inspect progress.
cli.cmd.contacts.action.description: |-
`start` to launch a contact pull or `status` to inspect the active job.
cli.cmd.contacts.pull.description: |-
Start pulling WhatsApp contacts into the inbox.
cli.cmd.contacts.pull.option.batch: |-
Number of contacts to pull in each batch.
cli.cmd.contacts.pull.option.progress: |-
Report progress every N contacts. Set to 0 to disable progress reports.
cli.cmd.contacts.pull.option.delay-contact: |-
Delay between syncing each contact to avoid rate limiting in pulling avatars.
cli.cmd.contacts.pull.option.delay-batch: |-
Delay between batches of contacts.
cli.cmd.contacts.pull.option.avatar: |-
Choose how to pull avatars for contacts.
cli.cmd.contacts.pull.option.groups: |-
Include WhatsApp group contacts in the sync.
cli.cmd.contacts.pull.option.lids: |-
Include WhatsApp LID contacts (anonymous contacts) in the sync.
cli.cmd.contacts.pull.option.no-attributes: |-
Do not pull contact attributes if contact already exists (it speeds up the process).
cli.cmd.contacts.pull.already-running: |-
⏳ **Pull Contacts** job is already running. Wait for it to finish or stop it before starting a new one.
Send `contacts status` for details.
cli.cmd.options.job.attempts: |-
Number of times to retry the job before marking it as failed.
cli.cmd.options.job.timeout: |-
Maximum time the job is allowed to run before it is retried (supports values like `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Show the status of the current pull job.
cli.cmd.contacts.status.not-found: |-
ℹ️ No active **Pull Contacts** job found. Start one with `contacts pull`.
cli.cmd.server.description: |-
Manage the server.
cli.cmd.server.status.description: |-
Show the server version and status.
cli.cmd.server.reboot.description: |-
Reboot the server.
cli.cmd.server.reboot.option.force: |-
Force an immediate reboot.
cli.help.usage.title: |-
Send a command to the conversation in the format:
cli.help.commands.defaultGroup: |-
**Commands:**
cli.help.arguments.title: |-
**Arguments:**
cli.help.options.defaultGroup: |-
**Options:**
cli.help.globalOptions.title: |-
**Global Options:**
task.contacts.started: |-
👤⏳ Pulling contacts... Send "contacts status" for more details.
task.contacts.progress: |-
👤⏳ Contacts: 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪ {{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.contacts.completed: |-
👤✅ Contacts Completed → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪ {{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.contacts.status: |-
👤 Contacts: **{{state}}**
- 🆕 Created: {{{progress.created}}} contacts
- 🔄 Updated: {{{progress.updated}}} contacts
- ⚪ Skipped: {{{progress.skipped}}} contacts
- 🔴 Errors: {{{progress.errors}}} contacts
- 🖼️ Avatar Updated: {{{progress.avatar.updated}}} contacts
Details:
[{{{details.text}}}]({{{details.url}}})
task.messages.started: |-
⏳ Pulling messages 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details: |-
Send "{{{prefix}}}messages status" to see more details.
task.messages.progress: |-
⏳ Messages {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅 {{{messages.last}}}{{/messages.last}}
task.messages.completed: |-
✅ Messages {{{chat}}} in {{{period}}} → 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Messages **{{{chat}}}** in ⏱️ **{{{period}}}** period. ⚙️ Job State: **{{state}}**.
🗨️ Messages:
- 🆕 Created: **{{{progress.ok}}}**
- 🟢 Already exists: {{{progress.exists}}}
- ⚪ Ignored: {{{progress.ignored}}}
- 🔴 Errors: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Earliest: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Latest: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp Chats:
- 🆕 New messages in **{{{chats}}}** chats
{{#options.resolveConversations}}
🗣️ Conversations:
- 🟢 Resolved: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Errors: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Details:
[{{{details.text}}}]({{{details.url}}})
app.help.reminder: |-
Send **help** to get the list of available commands.
@@ -303,17 +654,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ This ChatWoot Inbox is linked to **WhatsApp Account** and ready to send and receive messages!
✅ This inbox is linked to your **WhatsApp Account** and ready to send and receive messages!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **This ChatWoot Inbox is linked to WhatsApp Account but NOT ready to send and receive messages!**
⚠️ **This inbox is linked to your 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: |-
⚠️ **Scan the QR code on your phone to link WhatsApp Account with this ChatWoot Inbox!**
⚠️ **Scan the QR code on your phone to link your WhatsApp Account with this inbox!**
app.server.version.and.status: |-
**Version:**
+364 -40
View File
@@ -1,4 +1,4 @@
# Spanish translations for ChatWoot integration
# Spanish translations for WhatsApp inbox integration
locale.name: '🇪🇸 Español (Spanish)'
# Código de locale base - debe ser un locale BCP 47 válido
@@ -6,23 +6,28 @@ locale.name: '🇪🇸 Español (Spanish)'
# WAHA lo utiliza para formatear números, moneda y fecha/hora
locale.base: 'es-ES'
app.inbox.contact.name: 'Integración de WhatsApp (WAHA)'
app.inbox.contact.name: 'Integración de WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opciones" de date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: es-ES
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Madrid'
Today: 'Hoy'
Yesterday: 'Ayer'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ 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: |-
{{{text}}}
*📱 Enviado desde WhatsApp*
📱 *Enviado desde WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Enviado a través de API*
📱 *Enviado a través de API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Este mensaje fue eliminado en WhatsApp*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Lista de difusión'
whatsapp.contact.status.name: 'Estado'
app.connected.message: |-
🔗 **CONECTADO: Sesión de WhatsApp a Bandeja de Entrada de ChatWoot**
🔗 **CONECTADO: Sesión de WhatsApp a la bandeja de entrada**
✅ La sesión de WhatsApp **{{{name}}}** ha sido conectada exitosamente a esta Bandeja de Entrada de ChatWoot.
✅ La sesión de WhatsApp **{{{name}}}** ha sido conectada exitosamente a esta bandeja de entrada.
Esta bandeja de entrada ahora está vinculada a tu **Cuenta de WhatsApp**. Puedes enviar y recibir mensajes directamente desde aquí — justo como usar WhatsApp, pero dentro de ChatWoot.
Esta bandeja de entrada ahora está vinculada a tu **Cuenta de WhatsApp**. Puedes enviar y recibir mensajes directamente desde aquí — justo como usar WhatsApp, pero dentro de tu espacio de trabajo.
También puedes gestionar la sesión de WhatsApp vinculada a esta bandeja de entrada. Para ver las acciones disponibles, envía **help** a este chat.
@@ -104,9 +109,9 @@ app.connected.message: |-
¡Comencemos! 🚀
app.disconnected.message: |-
🔌 **DESCONECTADO: Sesión de WhatsApp de Bandeja de Entrada de ChatWoot**
🔌 **DESCONECTADO: Sesión de WhatsApp de la bandeja de entrada**
❌ La sesión de WhatsApp **{{{name}}}** ha sido desconectada de esta Bandeja de Entrada de ChatWoot.
❌ La sesión de WhatsApp **{{{name}}}** ha sido desconectada de esta bandeja de entrada.
Ya no podrás enviar o recibir mensajes desde WhatsApp.
@@ -117,37 +122,156 @@ app.updated.message: |-
✅ La configuración para la sesión de WhatsApp **{{{name}}}** ha sido actualizada.
app.commands.list: |-
Puedes gestionar la **Integración de WhatsApp** enviando uno de los siguientes comandos:
cli.usage.arguments: |-
[acción]
🖥️ **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.
4. **stop** - detener la sesión.
5. **logout** - cerrar sesión, para que puedas escanear el código QR y reparar la sesión.
6. **qr** - obtener el código QR para la sesión.
7. **screenshot** - obtener una captura de pantalla de la sesión (solo disponible en el motor **WEBJS**).
8. **help** - comandos disponibles.
cli.usage.command: |-
[comando]
{{#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}}
cli.usage.options: |-
[opciones]
Envía un **comando** (no sensible a mayúsculas/minúsculas) o un **número de comando** a esta conversación.
app.commands.server.disabled: ⛔ **Los comandos del servidor están
deshabilitados**.
cli.cmd.disabled: ⛔ **El comando `{{command}}` está deshabilitado**.
Por favor, contacta a tu administrador.
Envía **help** para obtener la lista de comandos disponibles.
cli.cmd.root.description: |-
¡Administra tu **integración de WhatsApp**!
cli.cmd.root.sub.session: |-
🖥️ **Sesión**:
cli.cmd.root.sub.server: |-
🖥️ **Servidor**:
cli.cmd.root.sub.sync: |-
🔄️ **Sincronización**:
cli.cmd.session.status.description: |-
Obtén el estado actual de la sesión.
cli.cmd.session.restart.description: |-
Reinicia la sesión.
cli.cmd.session.start.description: |-
Inicia la sesión.
cli.cmd.session.stop.description: |-
Detén la sesión.
cli.cmd.session.logout.description: |-
Cierra la sesión para escanear un nuevo código QR y restaurar la conexión.
cli.cmd.session.qr.description: |-
Muestra el código QR de la sesión.
cli.cmd.session.screenshot.description: |-
Captura una imagen de la sesión (disponible solo con el motor **WEBJS**).
cli.cmd.contacts.summary: |-
Obtener contactos de WhatsApp.
cli.cmd.contacts.description: |-
👤 Ejecuta `contacts pull` para iniciar una sincronización o `contacts status` para revisar el progreso.
cli.cmd.contacts.action.description: |-
Usa `start` para comenzar la extracción de contactos o `status` para revisar el trabajo activo.
cli.cmd.contacts.pull.description: |-
Empieza a importar contactos de WhatsApp en la bandeja de entrada.
cli.cmd.contacts.pull.option.batch: |-
Cantidad de contactos a extraer en cada lote.
cli.cmd.contacts.pull.option.delay-contact: |-
Retraso entre cada contacto para evitar límites al obtener los avatares.
cli.cmd.contacts.pull.option.delay-batch: |-
Retraso entre los lotes de contactos.
cli.cmd.contacts.pull.option.avatar: |-
Elige cómo obtener los avatares de los contactos: `skip`, `if-missing` o `update`.
cli.cmd.contacts.pull.option.groups: |-
Incluir contactos de grupos de WhatsApp en la sincronización.
cli.cmd.contacts.pull.option.no-attributes: |-
No obtengas los atributos del contacto si ya existe (acelera el proceso).
cli.cmd.contacts.pull.already-running: |-
⏳ El trabajo **Pull Contacts** ya se está ejecutando. Espera a que finalice o deténlo antes de iniciar uno nuevo.
Envía `contacts status` para más detalles.
cli.cmd.options.job.attempts: |-
Número de intentos antes de marcar el trabajo como fallido.
cli.cmd.options.job.timeout: |-
Tiempo máximo que puede ejecutarse el trabajo antes de reintentarlo (admite valores como `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Mostrar el estado del trabajo de importación actual.
cli.cmd.contacts.status.not-found: |-
ℹ️ No se encontró ningún trabajo activo de **Pull Contacts**. Inicia uno con `contacts pull`.
cli.cmd.server.description: |-
Administra el servidor.
cli.cmd.server.status.description: |-
Muestra la versión y el estado del servidor.
cli.cmd.server.reboot.description: |-
Reinicia el servidor.
cli.cmd.server.reboot.option.force: |-
Forzar un reinicio inmediato.
cli.help.usage.title: |-
Uso: envía un comando a la conversación en el formato:
cli.help.commands.defaultGroup: |-
**Comandos:**
cli.help.arguments.title: |-
**Argumentos:**
cli.help.options.defaultGroup: |-
**Opciones:**
cli.help.globalOptions.title: |-
**Opciones globales:**
task.contacts.status: |-
👤 Extracción de contactos: **{{state}}**
- ✅ Correctos: {{{progress.ok}}} contactos
- ⚪ Omitidos: {{{progress.skipped}}} contactos
- ⚠️ Errores: {{{progress.errors}}} contactos
{{#job}}
⌛ Cronología:
- En cola: {{{timestamp}}}
{{#processedOn}}
- Inicio: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Finalizó: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Detalles:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Extrayendo contactos...
task.contacts.progress: |-
Extracción de contactos: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Envía **help** para obtener la lista de comandos disponibles.
@@ -161,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ ¡Esta Bandeja de Entrada de ChatWoot está vinculada a la **Cuenta de WhatsApp** y lista para enviar y recibir mensajes!
✅ ¡Esta bandeja de entrada está vinculada a tu **Cuenta de WhatsApp** y lista para enviar y recibir mensajes!
📱 **{{{name}}} ({{{id}}})**
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!**
⚠️ **¡Esta bandeja de entrada 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: |-
⚠️ **¡Escanea el código QR en tu teléfono para vincular la Cuenta de WhatsApp con esta Bandeja de Entrada de ChatWoot!**
⚠️ **¡Escanea el código QR en tu teléfono para vincular la Cuenta de WhatsApp con esta bandeja de entrada!**
app.server.version.and.status: |-
**Versión:**
@@ -286,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -336,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Este tipo de mensaje no es compatible con ChatWoot.**
⚠️ **Este tipo de mensaje no es compatible con esta bandeja de entrada.**
📱 Por favor, abre **WhatsApp** para verlo.
Detalles: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Mensaje de Álbum Recibido**
{{#totalCount}}
Recibido un álbum con **{{totalCount}}** archivo(s) de medios.
{{#expectedImageCount}}
📸 Imágenes: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Videos: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Los archivos de medios se entregarán en los siguientes mensajes.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ANUNCIO DE FACEBOOK**
@@ -355,3 +522,160 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Mensaje del cliente:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Desconocido
ERROR: Error
PENDING: Enviando
SERVER: Servidor
DEVICE: Dispositivo
READ: Leído
PLAYED: Reproducido
whatsapp.to.chatwoot.message.has.media.no.media: >-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **El mensaje contiene contenido multimedia, pero no pudimos descargarlo**
📱 Abre **WhatsApp** para verlo.
{{^content}}
Detalles: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Cola**:'
cli.cmd.messages.summary: Extrae mensajes de WhatsApp.
cli.cmd.messages.description:
💬 Ejecuta `messages pull` para solicitar la sincronización de mensajes o
`messages status` para revisar el progreso.
cli.cmd.messages.action.description:
'`pull` para iniciar la extracción de mensajes o `status` para revisar el
trabajo activo.'
cli.cmd.messages.pull.option.chat:
Limita la extracción a un ID de chat de WhatsApp específico.
cli.cmd.messages.pull.option.force:
Obliga a la bandeja de entrada a crear mensajes incluso cuando el mensaje de
WhatsApp ya existe en el mapeo de la conversación.
cli.cmd.messages.pull.option.batch:
Número de mensajes que se extraen en cada lote.
cli.cmd.messages.pull.option.progress:
Informa el progreso cada N mensajes. Establécelo en 0 para desactivar los
informes de progreso.
cli.cmd.messages.pull.option.no-dm:
Omite los mensajes directos al extraer el historial.
cli.cmd.messages.pull.option.groups:
Incluye chats de grupo de WhatsApp al extraer el historial.
cli.cmd.messages.pull.option.channels:
Incluye canales de WhatsApp al extraer el historial.
cli.cmd.messages.pull.option.status:
Incluye actualizaciones de estado de WhatsApp al extraer el historial.
cli.cmd.messages.pull.option.broadcast:
Incluye listas de difusión al extraer el historial.
cli.cmd.messages.pull.option.media:
Descarga archivos multimedia (imágenes, videos, archivos) al extraer el
historial.
cli.cmd.messages.pull.option.pause:
Encola el trabajo de extracción de mensajes en estado pausado, deteniendo el
procesamiento de nuevos mensajes de WhatsApp hasta que finalice.
cli.cmd.messages.pull.option.resolve-conversations:
Marca las conversaciones como resueltas después de procesar todo el historial.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ No se puede usar `--pause` porque el comando **queue** está deshabilitado
en esta configuración de la aplicación.
cli.cmd.queue.description: Gestiona las colas.
cli.cmd.queue.summary: Realiza acciones sobre las colas.
cli.cmd.queue.action.description: Elige una acción.
cli.cmd.queue.status.description: Muestra el estado de una cola.
cli.cmd.queue.argument.name:
'Nombre de la cola: `inbox` o `whatsapp` (opcional).'
cli.cmd.queue.start.description: Inicia una cola por nombre.
cli.cmd.queue.stop.description: Detiene una cola por nombre.
cli.cmd.queue.paused:
⚪🧩 Las colas se han pausado. Envía `queue status` para comprobar el estado.
cli.cmd.queue.resumed:
🟢🧩 Las colas se han reanudado. Envía `queue status` para comprobar el
estado.
cli.cmd.queue.status.result: >-
📊 Estado de la cola:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ En
pausa{{/paused}}{{^paused}}🟢 En ejecución{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Define cuánto tiempo se intenta descargar los archivos multimedia de cada
mensaje.
cli.cmd.messages.pull.argument.end:
Define dónde termina la ventana de extracción; por ejemplo, `2d` se detiene
hace dos días.
cli.cmd.messages.pull.argument.start:
Define desde dónde comienza la ventana de extracción; por ejemplo, `1d`
empieza hace un día (el valor predeterminado es `0d`, es decir, ahora).
cli.cmd.messages.pull.already-running: >-
⏳ El trabajo **Pull Messages** ya se está ejecutando para **{{{chat}}}**.
Espera a que termine o deténlo antes de iniciar uno nuevo.
Envía `messages status` para obtener detalles.
cli.cmd.messages.status.not-found:
ℹ️ No se encontró ningún trabajo activo de **Pull Messages**. Inicia uno con
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
No se encontraron chats de WhatsApp de los que extraer mensajes en el período.
cli.cmd.contacts.pull.option.progress:
Informa el progreso cada N contactos. Establécelo en 0 para desactivar los
informes de progreso.
cli.cmd.contacts.pull.option.lids:
Incluye contactos LID de WhatsApp (contactos anónimos) en la sincronización.
task.contacts.completed:
👤✅ Contactos completados → 🆕 {{progress.created}}, 🔄 {{progress.updated}},
⚪ {{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️
{{progress.avatar.updated}}
task.messages.started: ⏳ Extrayendo mensajes 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Envía "{{{prefix}}}messages status" para ver más detalles.
task.messages.progress:
'⏳ Mensajes {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Mensajes {{{chat}}} en {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Mensajes **{{{chat}}}** en el periodo ⏱️ **{{{period}}}**. ⚙️ Estado del trabajo: **{{state}}**.
🗨️ Mensajes:
- 🆕 Creados: **{{{progress.ok}}}**
- 🟢 Ya existen: {{{progress.exists}}}
- ⚪ Ignorados: {{{progress.ignored}}}
- 🔴 Errores: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Más antiguo: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Más reciente: **{{{messages.last}}}**
{{/messages.last}}
💬 Chats de WhatsApp:
- 🆕 Nuevos mensajes en **{{{chats}}}** chats
{{#options.resolveConversations}}
🗣️ Conversaciones:
- 🟢 Resueltas: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Errores: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Detalles:
[{{{details.text}}}]({{{details.url}}})
+351 -37
View File
@@ -1,4 +1,4 @@
# Persian translations for ChatWoot integration
# Persian translations for WhatsApp inbox integration
locale.name: '🇮🇷 فارسی (Persian)'
# کد محلی پایه - باید یک محلی معتبر BCP 47 باشد
@@ -6,23 +6,28 @@ locale.name: '🇮🇷 فارسی (Persian)'
# WAHA از آن برای نمایش اعداد، ارز و تاریخ/زمان استفاده می‌کند
locale.base: 'fa-IR'
app.inbox.contact.name: 'ادغام واتساپ (WAHA)'
app.inbox.contact.name: 'ادغام واتساپ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# گزینه‌های "options" از date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: fa-IR
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Tehran'
Today: 'امروز'
Yesterday: 'دیروز'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: حذف پیام از واتساپ امکان‌
job.scheduled.error.header: اجرای کار زمان‌بندی شده ناموفق بود
message.from.whatsapp: |-
{{{text}}}
*📱 ارسال شده از واتساپ*
📱 *ارسال شده از واتساپ*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 ارسال شده از طریق API*
📱 *ارسال شده از طریق API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *این پیام در واتساپ حذف شده است*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'فهرست انتشار'
whatsapp.contact.status.name: 'وضعیت'
app.connected.message: |-
🔗 **متصل: جلسه واتساپ به صندوق ورودی ChatWoot**
🔗 **متصل: جلسه واتساپ به صندوق ورودی**
✅ جلسه واتساپ **{{{name}}}** با موفقیت به این صندوق ورودی ChatWoot متصل شده است.
✅ جلسه واتساپ **{{{name}}}** با موفقیت به این صندوق ورودی متصل شده است.
این صندوق ورودی اکنون به **حساب واتساپ** شما متصل است. شما می‌توانید پیام‌ها را مستقیماً از اینجا ارسال و دریافت کنید — درست مانند استفاده از واتساپ، اما در داخل ChatWoot.
این صندوق ورودی اکنون به **حساب واتساپ** شما متصل است. شما می‌توانید پیام‌ها را مستقیماً از اینجا ارسال و دریافت کنید — درست مانند استفاده از واتساپ، اما در فضای کاری شما.
همچنین می‌توانید جلسه واتساپ متصل به این صندوق ورودی را مدیریت کنید. برای مشاهده اقدامات موجود، **help** را به این گفتگو ارسال کنید.
@@ -104,9 +109,9 @@ app.connected.message: |-
بیایید شروع کنیم! 🚀
app.disconnected.message: |-
🔌 **قطع اتصال: جلسه واتساپ از صندوق ورودی ChatWoot**
🔌 **قطع اتصال: جلسه واتساپ از صندوق ورودی**
❌ جلسه واتساپ **{{{name}}}** از این صندوق ورودی ChatWoot قطع شده است.
❌ جلسه واتساپ **{{{name}}}** از این صندوق ورودی قطع شده است.
شما دیگر قادر به ارسال یا دریافت پیام از واتساپ نخواهید بود.
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ تنظیمات جلسه واتساپ **{{{name}}}** به‌روزرسانی شده است.
app.commands.list: |-
شما می‌توانید **ادغام واتساپ** را با ارسال یکی از دستورات زیر مدیریت کنید:
cli.usage.arguments: |-
[اقدام]
cli.usage.command: |-
[دستور]
cli.usage.options: |-
[گزینه‌ها]
cli.cmd.disabled: ⛔ **دستور `{{command}}` غیرفعال شده است**.
لطفاً با مدیر خود تماس بگیرید.
برای دریافت فهرست دستورات موجود، **help** را ارسال کنید.
cli.cmd.root.description: |-
یکپارچه‌سازی **واتساپ** خود را مدیریت کنید!
cli.cmd.root.sub.session: |-
🖥️ **جلسه**:
1. **status** - دریافت وضعیت فعلی جلسه.
2. **restart** - راه‌اندازی مجدد جلسه - این تلاش خواهد کرد حساب واتساپ شما را مجدداً متصل کند.
3. **start** - شروع جلسه.
4. **stop** - توقف جلسه.
5. **logout** - خروج از جلسه، تا بتوانید کد QR را اسکن کرده و جلسه را تعمیر کنید.
6. **qr** - دریافت کد QR برای جلسه.
7. **screenshot** - دریافت اسکرین‌شات از جلسه (فقط در موتور **WEBJS** در دسترس است).
8. **help** - دستورات موجود.
{{#commands.server}}
⚙️ سرور:
- **server status** - دریافت اطلاعات نسخه و وضعیت سرور.
- **server reboot** - راه‌اندازی مجدد سرور به صورت نرم.
- **server reboot force** - راه‌اندازی مجدد اجباری سرور.
{{/commands.server}}
cli.cmd.root.sub.server: |-
🖥️ **سرور**:
یک **دستور** (بدون حساسیت به بزرگی و کوچکی حروف) یا **شماره دستور** را به این گفتگو ارسال کنید.
cli.cmd.root.sub.sync: |-
🔄️ **همگام‌سازی**:
app.commands.server.disabled: ⛔ **دستورات سرور غیرفعال است**.
cli.cmd.session.status.description: |-
وضعیت فعلی جلسه را دریافت کنید.
لطفاً با مدیر سیستم خود تماس بگیرید.
cli.cmd.session.restart.description: |-
جلسه را مجدداً راه‌اندازی کنید.
برای دریافت فهرست دستورات، **help** را ارسال کنید.
cli.cmd.session.start.description: |-
جلسه را شروع کنید.
cli.cmd.session.stop.description: |-
جلسه را متوقف کنید.
cli.cmd.session.logout.description: |-
برای اسکن کد QR جدید و بازیابی اتصال از جلسه خارج شوید.
cli.cmd.session.qr.description: |-
کد QR جلسه را نمایش دهید.
cli.cmd.session.screenshot.description: |-
از جلسه اسکرین‌شات بگیرید (فقط با موتور **WEBJS** در دسترس است).
cli.cmd.contacts.summary: |-
دریافت مخاطبان واتساپ.
cli.cmd.contacts.description: |-
👤 `contacts pull` را اجرا کنید تا همگام‌سازی آغاز شود یا `contacts status` را برای بررسی پیشرفت اجرا کنید.
cli.cmd.contacts.action.description: |-
از `start` برای آغاز دریافت مخاطبان یا از `status` برای بررسی کار فعال استفاده کنید.
cli.cmd.contacts.pull.description: |-
دریافت مخاطبان واتساپ را به این صندوق ورودی آغاز کنید.
cli.cmd.contacts.pull.option.batch: |-
تعداد مخاطبانی که در هر دسته دریافت می‌شوند.
cli.cmd.contacts.pull.option.delay-contact: |-
تأخیر بین همگام‌سازی هر مخاطب برای جلوگیری از محدودیت نرخ هنگام دریافت آواتار.
cli.cmd.contacts.pull.option.delay-batch: |-
تأخیر بین دسته‌های مخاطبان.
cli.cmd.contacts.pull.option.avatar: |-
نحوه دریافت آواتار مخاطبان را انتخاب کنید: `skip`، `if-missing` یا `update`.
cli.cmd.contacts.pull.option.groups: |-
مخاطبان گروه‌های واتساپ را در همگام‌سازی قرار دهید.
cli.cmd.contacts.pull.option.no-attributes: |-
اگر مخاطب از قبل وجود دارد ویژگی‌های او را دریافت نکنید (فرآیند را سریع‌تر می‌کند).
cli.cmd.contacts.pull.already-running: |-
⏳ کار **Pull Contacts** هم‌اکنون در حال اجراست. صبر کنید تا تمام شود یا قبل از شروع کار جدید آن را متوقف کنید.
برای جزئیات `contacts status` را ارسال کنید.
cli.cmd.options.job.attempts: |-
تعداد دفعات تلاش مجدد پیش از علامت‌گذاری کار به‌عنوان ناموفق.
cli.cmd.options.job.timeout: |-
حداکثر مدت اجرای کار پیش از تلاش دوباره (از مقادیری مانند `30s`، `5m`، `1h` پشتیبانی می‌کند).
cli.cmd.contacts.status.description: |-
وضعیت کار دریافت فعلی را نمایش دهید.
cli.cmd.contacts.status.not-found: |-
ℹ️ هیچ کار فعال **Pull Contacts** یافت نشد. با `contacts pull` یکی آغاز کنید.
cli.cmd.server.description: |-
سرور را مدیریت کنید.
cli.cmd.server.status.description: |-
نسخه و وضعیت سرور را نمایش دهید.
cli.cmd.server.reboot.description: |-
سرور را راه‌اندازی مجدد کنید.
cli.cmd.server.reboot.option.force: |-
راه‌اندازی مجدد فوری را اجباری کنید.
cli.help.usage.title: |-
نحوه استفاده: دستور را با قالب زیر به گفتگو ارسال کنید:
cli.help.commands.defaultGroup: |-
**دستورات:**
cli.help.arguments.title: |-
**آرگومان‌ها:**
cli.help.options.defaultGroup: |-
**گزینه‌ها:**
cli.help.globalOptions.title: |-
**گزینه‌های عمومی:**
task.contacts.status: |-
👤 دریافت مخاطبان: **{{state}}**
- ✅ موفق: {{{progress.ok}}} مخاطب
- ⚪ نادیده‌گرفته‌شده: {{{progress.skipped}}} مخاطب
- ⚠️ خطا: {{{progress.errors}}} مخاطب
{{#job}}
⌛ جدول زمانی:
- در صف: {{{timestamp}}}
{{#processedOn}}
- زمان شروع: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- زمان پایان: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
جزئیات:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
در حال دریافت مخاطبان...
task.contacts.progress: |-
دریافت مخاطبان: ✅ {{progress.ok}}، ⚪ {{progress.skipped}}، ⚠️ {{progress.errors}}
app.help.reminder: |-
برای دریافت لیست دستورات موجود، **help** را ارسال کنید.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ این صندوق ورودی ChatWoot به **حساب واتساپ** متصل است و آماده ارسال و دریافت پیام است!
✅ این صندوق ورودی به **حساب واتساپ** شما متصل است و آماده ارسال و دریافت پیام است!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **این صندوق ورودی ChatWoot به حساب واتساپ متصل است اما آماده ارسال و دریافت پیام نیست!**
⚠️ **این صندوق ورودی به حساب واتساپ متصل است اما آماده ارسال و دریافت پیام نیست!**
لطفاً وضعیت جلسه را بررسی کنید و سعی کنید آن را مجدداً راه‌اندازی کنید یا خارج شوید و کد QR را دوباره اسکن کنید.
app.session.scan.qr.code: |-
⚠️ **کد QR را در تلفن خود اسکن کنید تا حساب واتساپ را با این صندوق ورودی ChatWoot پیوند دهید!**
⚠️ **کد QR را در تلفن خود اسکن کنید تا حساب واتساپ را با این صندوق ورودی پیوند دهید!**
app.server.version.and.status: |-
**نسخه:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **این نوع پیام در ChatWoot پشتیبانی نمی‌شود.**
⚠️ **این نوع پیام در این صندوق ورودی پشتیبانی نمی‌شود.**
📱 لطفاً برای مشاهده آن **WhatsApp** را باز کنید.
جزئیات: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **پیام آلبوم دریافت شد**
{{#totalCount}}
یک آلبوم با **{{totalCount}}** فایل رسانه‌ای دریافت شد.
{{#expectedImageCount}}
📸 تصاویر: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 ویدیوها: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
فایل‌های رسانه‌ای در پیام‌های بعدی تحویل داده خواهند شد.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **آگهی فیس‌بوک**
@@ -352,3 +522,147 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **پیام مشتری:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: ناشناخته
ERROR: خطا
PENDING: در حال ارسال
SERVER: سرور
DEVICE: دستگاه
READ: خوانده شد
PLAYED: پخش شد
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **پیام حاوی رسانه است اما نتوانستیم آن را دانلود کنیم**
📱 لطفاً برای مشاهده، **واتساپ** را باز کنید.
{{^content}}
جزئیات: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **صف**:'
cli.cmd.messages.summary: دریافت پیام‌های واتساپ.
cli.cmd.messages.description:
💬 برای آغاز همگام‌سازی پیام‌ها `messages pull` را اجرا کنید یا برای مشاهده
پیشرفت `messages status` را اجرا کنید.
cli.cmd.messages.action.description:
'`pull` برای شروع دریافت پیام یا `status` برای بررسی کار فعال.'
cli.cmd.messages.pull.option.chat:
دریافت را به یک شناسه گفت‌وگوی مشخص واتساپ محدود می‌کند.
cli.cmd.messages.pull.option.force:
صندوق ورودی را مجبور می‌کند پیام بسازد حتی اگر پیام واتساپ قبلاً در نگاشت
مکالمه وجود داشته باشد.
cli.cmd.messages.pull.option.batch:
تعداد پیام‌هایی که در هر دسته دریافت می‌شوند.
cli.cmd.messages.pull.option.progress:
پیشرفت را هر N پیام اعلام می‌کند. مقدار 0 گزارش‌ها را غیرفعال می‌کند.
cli.cmd.messages.pull.option.no-dm:
هنگام دریافت تاریخچه، پیام‌های خصوصی را نادیده می‌گیرد.
cli.cmd.messages.pull.option.groups:
گروه‌های واتساپ را هنگام دریافت تاریخچه وارد می‌کند.
cli.cmd.messages.pull.option.channels:
کانال‌های واتساپ را هنگام دریافت تاریخچه وارد می‌کند.
cli.cmd.messages.pull.option.status:
وضعیت‌های واتساپ را هنگام دریافت تاریخچه وارد می‌کند.
cli.cmd.messages.pull.option.broadcast:
لیست‌های پخش را هنگام دریافت تاریخچه وارد می‌کند.
cli.cmd.messages.pull.option.media:
هنگام دریافت تاریخچه، پیوست‌های رسانه‌ای (تصویر، ویدیو، فایل) را دانلود
می‌کند.
cli.cmd.messages.pull.option.pause:
کار دریافت پیام‌ها را در حالت توقف در صف قرار می‌دهد و پردازش پیام‌های جدید
واتساپ را تا پایان متوقف می‌کند.
cli.cmd.messages.pull.option.resolve-conversations:
پس از پردازش تمام پیام‌های تاریخچه، مکالمه‌ها را به‌عنوان حل‌شده علامت‌گذاری
می‌کند.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ امکان استفاده از `--pause` نیست زیرا فرمان **queue** در این پیکربندی
غیرفعال است.
cli.cmd.queue.description: مدیریت صف‌ها.
cli.cmd.queue.summary: اعمال روی صف‌ها را انجام دهید.
cli.cmd.queue.action.description: یک عمل را انتخاب کنید.
cli.cmd.queue.status.description: وضعیت یک صف را نشان می‌دهد.
cli.cmd.queue.argument.name: 'نام صف: `inbox` یا `whatsapp` (اختیاری).'
cli.cmd.queue.start.description: صف را بر اساس نام شروع کنید.
cli.cmd.queue.stop.description: صف را بر اساس نام متوقف کنید.
cli.cmd.queue.paused:
⚪🧩 صف‌ها متوقف شده‌اند. برای بررسی وضعیت `queue status` را ارسال کنید.
cli.cmd.queue.resumed:
🟢🧩 صف‌ها از سر گرفته شدند. برای بررسی وضعیت `queue status` را ارسال کنید.
cli.cmd.queue.status.result: >-
📊 وضعیت صف:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
متوقف{{/paused}}{{^paused}}🟢 فعال{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
مدت تلاش برای دانلود رسانه هر پیام را مشخص می‌کند.
cli.cmd.messages.pull.argument.end:
پایان بازه دریافت را تعیین می‌کند؛ برای مثال `2d` یعنی دو روز پیش متوقف
می‌شود.
cli.cmd.messages.pull.argument.start:
آغاز بازه دریافت را تعیین می‌کند؛ برای مثال `1d` یعنی یک روز پیش (پیش‌فرض `0d`
یعنی اکنون).
cli.cmd.messages.pull.already-running: >-
⏳ کار **Pull Messages** هم‌اکنون برای **{{{chat}}}** در حال اجراست. منتظر
بمانید تا تمام شود یا قبل از شروع کار جدید، آن را متوقف کنید.
برای جزئیات `messages status` را ارسال کنید.
cli.cmd.messages.status.not-found:
ℹ️ هیچ کار فعال **Pull Messages** پیدا نشد. با `messages pull` یک کار را آغاز
کنید.
cli.cmd.messages.pull.no-chats-found:
هیچ گفت‌وگوی واتساپی برای این بازه زمانی پیدا نشد.
cli.cmd.contacts.pull.option.progress:
پیشرفت را هر N مخاطب اعلام می‌کند. مقدار 0 گزارش‌ها را غیرفعال می‌کند.
cli.cmd.contacts.pull.option.lids:
مخاطبین ناشناس واتساپ (LID) را در همگام‌سازی وارد می‌کند.
task.contacts.completed:
👤✅ مخاطبین تکمیل شدند → 🆕 {{progress.created}}، 🔄 {{progress.updated}}، ⚪
{{progress.skipped}}، 🔴 {{progress.errors}}، 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ در حال دریافت پیام‌ها 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
برای جزئیات بیشتر "{{{prefix}}}messages status" را ارسال کنید.
task.messages.progress:
'⏳ پیام‌های {{{chat}}}: 🆕 {{progress.ok}}، 🟢 {{progress.exists}}، ⚪
{{progress.ignored}}، 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ پیام‌های {{{chat}}} در {{{period}}} → 🆕 {{progress.ok}}، 🟢
{{progress.exists}}، ⚪ {{progress.ignored}}، 🔴 {{progress.errors}}
task.messages.status: |-
پیام‌های **{{{chat}}}** در بازه ⏱️ **{{{period}}}**. ⚙️ وضعیت کار: **{{state}}**.
🗨️ پیام‌ها:
- 🆕 ایجاد شده: **{{{progress.ok}}}**
- 🟢 موجود: {{{progress.exists}}}
- ⚪ نادیده گرفته شده: {{{progress.ignored}}}
- 🔴 خطا: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ قدیمی‌ترین: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ جدیدترین: **{{{messages.last}}}**
{{/messages.last}}
💬 گفت‌وگوهای واتساپ:
- 🆕 پیام‌های جدید در **{{{chats}}}** گفت‌وگو
{{#options.resolveConversations}}
🗣️ مکالمه‌ها:
- 🟢 حل شد: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 خطا: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
جزئیات:
[{{{details.text}}}]({{{details.url}}})
+364 -37
View File
@@ -1,4 +1,4 @@
# French translations for ChatWoot integration
# French translations for WhatsApp inbox integration
locale.name: '🇫🇷 Français (French)'
# Code de locale de base - doit être une locale BCP 47 valide
@@ -6,23 +6,28 @@ locale.name: '🇫🇷 Français (French)'
# WAHA l'utilise pour le formatage des nombres, devises et dates/heures
locale.base: 'fr-FR'
app.inbox.contact.name: 'Intégration WhatsApp (WAHA)'
app.inbox.contact.name: 'Intégration WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" de date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: fr-FR
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Paris'
Today: "Aujourd'hui"
Yesterday: 'Hier'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ 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: |-
{{{text}}}
*📱 Envoyé depuis WhatsApp*
📱 *Envoyé depuis WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Envoyé via API*
📱 *Envoyé via API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Ce message a été supprimé dans WhatsApp*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Liste de diffusion'
whatsapp.contact.status.name: 'Statut'
app.connected.message: |-
🔗 **CONNECTÉ : Session WhatsApp à la Boîte de Réception ChatWoot**
🔗 **CONNECTÉ : Session WhatsApp à la boîte de réception**
✅ La session WhatsApp **{{{name}}}** a été connectée avec succès à cette Boîte de Réception ChatWoot.
✅ La session WhatsApp **{{{name}}}** a été connectée avec succès à cette boîte de réception.
Cette boîte de réception est maintenant liée à votre **Compte WhatsApp**. Vous pouvez envoyer et recevoir des messages directement d'ici — tout comme utiliser WhatsApp, mais à l'intérieur de ChatWoot.
Cette boîte de réception est maintenant liée à votre **Compte WhatsApp**. Vous pouvez envoyer et recevoir des messages directement d'ici — tout comme utiliser WhatsApp, mais dans votre espace de travail.
Vous pouvez également gérer la session WhatsApp liée à cette boîte de réception. Pour voir les actions disponibles, envoyez **help** à ce chat.
@@ -104,9 +109,9 @@ app.connected.message: |-
Commençons ! 🚀
app.disconnected.message: |-
🔌 **DÉCONNECTÉ : Session WhatsApp de la Boîte de Réception ChatWoot**
🔌 **DÉCONNECTÉ : Session WhatsApp de la boîte de réception**
❌ La session WhatsApp **{{{name}}}** a été déconnectée de cette Boîte de Réception ChatWoot.
❌ La session WhatsApp **{{{name}}}** a été déconnectée de cette boîte de réception.
Vous ne pourrez plus envoyer ou recevoir de messages depuis WhatsApp.
@@ -117,34 +122,156 @@ app.updated.message: |-
✅ Les paramètres de la session WhatsApp **{{{name}}}** ont été mis à jour.
app.commands.list: |-
Vous pouvez gérer l'**Intégration WhatsApp** en envoyant l'une des commandes suivantes :
cli.usage.arguments: |-
[action]
🖥️ **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.
4. **stop** - arrêter la session.
5. **logout** - se déconnecter de la session, pour pouvoir scanner le code QR et réparer la session.
6. **qr** - obtenir le code QR pour la session.
7. **screenshot** - obtenir une capture d'écran de la session (disponible uniquement dans le moteur **WEBJS**).
8. **help** - commandes disponibles.
cli.usage.command: |-
[commande]
{{#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}}
cli.usage.options: |-
[options]
Envoyez une **commande** (insensible à la casse) ou un **numéro de commande** à cette conversation.
app.commands.server.disabled: ⛔ **Les commandes serveur sont désactivées**.
cli.cmd.disabled: ⛔ **La commande `{{command}}` est désactivée**.
Veuillez contacter votre administrateur.
Envoyez **help** pour obtenir la liste des commandes disponibles.
cli.cmd.root.description: |-
Gérez votre **intégration WhatsApp** !
cli.cmd.root.sub.session: |-
🖥️ **Session** :
cli.cmd.root.sub.server: |-
🖥️ **Serveur** :
cli.cmd.root.sub.sync: |-
🔄️ **Synchronisation** :
cli.cmd.session.status.description: |-
Obtenez l'état actuel de la session.
cli.cmd.session.restart.description: |-
Redémarrez la session.
cli.cmd.session.start.description: |-
Démarrez la session.
cli.cmd.session.stop.description: |-
Arrêtez la session.
cli.cmd.session.logout.description: |-
Déconnectez la session pour scanner un nouveau code QR et rétablir la connexion.
cli.cmd.session.qr.description: |-
Affichez le code QR de la session.
cli.cmd.session.screenshot.description: |-
Prenez une capture d'écran de la session (disponible uniquement avec le moteur **WEBJS**).
cli.cmd.contacts.summary: |-
Importer les contacts WhatsApp.
cli.cmd.contacts.description: |-
👤 Exécutez `contacts pull` pour lancer une synchronisation ou `contacts status` pour suivre la progression.
cli.cmd.contacts.action.description: |-
Utilisez `start` pour démarrer l'import de contacts ou `status` pour consulter la tâche active.
cli.cmd.contacts.pull.description: |-
Commencez à importer les contacts WhatsApp dans la boîte de réception.
cli.cmd.contacts.pull.option.batch: |-
Nombre de contacts importés par lot.
cli.cmd.contacts.pull.option.delay-contact: |-
Délai entre chaque contact pour éviter les limites lors du chargement des avatars.
cli.cmd.contacts.pull.option.delay-batch: |-
Délai entre les lots de contacts.
cli.cmd.contacts.pull.option.avatar: |-
Choisissez comment charger les avatars des contacts : `skip`, `if-missing` ou `update`.
cli.cmd.contacts.pull.option.groups: |-
Inclure les contacts de groupes WhatsApp dans la synchronisation.
cli.cmd.contacts.pull.option.no-attributes: |-
Ne pas importer les attributs si le contact existe déjà (accélère le processus).
cli.cmd.contacts.pull.already-running: |-
⏳ La tâche **Pull Contacts** est déjà en cours. Attendez qu'elle se termine ou arrêtez-la avant d'en démarrer une nouvelle.
Envoyez `contacts status` pour plus de détails.
cli.cmd.options.job.attempts: |-
Nombre de tentatives avant de marquer la tâche comme échouée.
cli.cmd.options.job.timeout: |-
Durée maximale autorisée avant une nouvelle tentative (accepte `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Afficher l'état de la tâche d'import en cours.
cli.cmd.contacts.status.not-found: |-
ℹ️ Aucune tâche **Pull Contacts** active trouvée. Démarrez-en une avec `contacts pull`.
cli.cmd.server.description: |-
Gérez le serveur.
cli.cmd.server.status.description: |-
Affichez la version et l'état du serveur.
cli.cmd.server.reboot.description: |-
Redémarrez le serveur.
cli.cmd.server.reboot.option.force: |-
Forcer un redémarrage immédiat.
cli.help.usage.title: |-
Utilisation : envoyez une commande à la conversation au format suivant :
cli.help.commands.defaultGroup: |-
**Commandes :**
cli.help.arguments.title: |-
**Arguments :**
cli.help.options.defaultGroup: |-
**Options :**
cli.help.globalOptions.title: |-
**Options globales :**
task.contacts.status: |-
👤 Import de contacts : **{{state}}**
- ✅ Réussis : {{{progress.ok}}} contacts
- ⚪ Ignorés : {{{progress.skipped}}} contacts
- ⚠️ Erreurs : {{{progress.errors}}} contacts
{{#job}}
⌛ Chronologie :
- En file d'attente : {{{timestamp}}}
{{#processedOn}}
- Début : {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Fin : {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Détails :
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Importation des contacts...
task.contacts.progress: |-
Import de contacts : ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Envoyez **help** pour obtenir la liste des commandes disponibles.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
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 !
✅ Cette boîte de réception est liée à votre **Compte WhatsApp** et prête à envoyer et recevoir des messages !
📱 **{{{name}}} ({{{id}}})**
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 !**
⚠️ **Cette boîte de réception 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: |-
⚠️ **Scannez le code QR sur votre téléphone pour lier le Compte WhatsApp à cette Boîte de Réception ChatWoot !**
⚠️ **Scannez le code QR sur votre téléphone pour lier le Compte WhatsApp à cette boîte de réception !**
app.server.version.and.status: |-
**Version :**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Ce type de message n’est pas pris en charge dans ChatWoot.**
⚠️ **Ce type de message n’est pas pris en charge dans cette boîte de réception.**
📱 Veuillez ouvrir **WhatsApp** pour l’afficher.
Détails: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Message d'Album Reçu**
{{#totalCount}}
Reçu un album avec **{{totalCount}}** fichier(s) multimédia.
{{#expectedImageCount}}
📸 Images: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Vidéos: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Les fichiers multimédias seront livrés dans les messages suivants.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **PUBLICITÉ FACEBOOK**
@@ -352,3 +522,160 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Message du client :**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Inconnu
ERROR: Erreur
PENDING: Envoi en cours
SERVER: Serveur
DEVICE: Appareil
READ: Lu
PLAYED: Écouté
whatsapp.to.chatwoot.message.has.media.no.media: >-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **Le message contient un média, mais nous n’avons pas pu le télécharger**
📱 Ouvrez **WhatsApp** pour le consulter.
{{^content}}
Détails : [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **File** :'
cli.cmd.messages.summary: Récupérer les messages WhatsApp.
cli.cmd.messages.description:
💬 Exécutez `messages pull` pour lancer la synchronisation ou `messages
status` pour suivre l’avancement.
cli.cmd.messages.action.description:
'`pull` pour lancer la récupération ou `status` pour inspecter la tâche en
cours.'
cli.cmd.messages.pull.option.chat:
Limite la récupération à un identifiant de conversation WhatsApp précis.
cli.cmd.messages.pull.option.force:
Force la boîte de réception à créer des messages même si le message WhatsApp
existe déjà dans le mappage de la conversation.
cli.cmd.messages.pull.option.batch: Nombre de messages récupérés à chaque lot.
cli.cmd.messages.pull.option.progress:
Annonce la progression toutes les N messages. Mettre 0 pour désactiver les
rapports.
cli.cmd.messages.pull.option.no-dm:
Ignore les messages directs lors de la récupération de l’historique.
cli.cmd.messages.pull.option.groups:
Inclut les groupes WhatsApp lors de la récupération de l’historique.
cli.cmd.messages.pull.option.channels:
Inclut les canaux WhatsApp lors de la récupération de l’historique.
cli.cmd.messages.pull.option.status:
Inclut les statuts WhatsApp lors de la récupération de l’historique.
cli.cmd.messages.pull.option.broadcast:
Inclut les listes de diffusion lors de la récupération de l’historique.
cli.cmd.messages.pull.option.media:
Télécharge les pièces jointes (images, vidéos, fichiers) lors de la
récupération.
cli.cmd.messages.pull.option.pause:
Met la tâche de récupération en file d’attente à l’arrêt, suspendant les
nouveaux messages WhatsApp jusqu’à la fin.
cli.cmd.messages.pull.option.resolve-conversations:
Marque les conversations comme résolues après le traitement de tout
l’historique.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ Impossible d’utiliser `--pause` car la commande **queue** est désactivée
dans cette configuration.
cli.cmd.queue.description: Gérer les files.
cli.cmd.queue.summary: Effectuer des actions sur les files.
cli.cmd.queue.action.description: Choisissez une action.
cli.cmd.queue.status.description: Affiche l’état d’une file.
cli.cmd.queue.argument.name:
'Nom de la file : `inbox` ou `whatsapp` (facultatif).'
cli.cmd.queue.start.description: Démarre une file par son nom.
cli.cmd.queue.stop.description: Arrête une file par son nom.
cli.cmd.queue.paused:
⚪🧩 Les files ont été mises en pause. Envoyez `queue status` pour vérifier
l’état.
cli.cmd.queue.resumed:
🟢🧩 Les files ont repris. Envoyez `queue status` pour vérifier l’état.
cli.cmd.queue.status.result: >-
📊 État de la file :
{{#queues}}
- **{{name}}** : {{#locked}}🔒{{/locked}}{{#paused}}⚪ En
pause{{/paused}}{{^paused}}🟢 En cours{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Définit la durée d’une tentative de téléchargement des médias de chaque
message.
cli.cmd.messages.pull.argument.end:
Définit la fin de la fenêtre de récupération, par exemple `2d` s’arrête deux
jours en arrière.
cli.cmd.messages.pull.argument.start:
Définit le début de la fenêtre, par exemple `1d` commence un jour en arrière
(par défaut `0d`, maintenant).
cli.cmd.messages.pull.already-running: >-
⏳ La tâche **Pull Messages** est déjà en cours pour **{{{chat}}}**. Patientez
jusqu’à la fin ou arrêtez-la avant d’en lancer une nouvelle.
Envoyez `messages status` pour plus de détails.
cli.cmd.messages.status.not-found:
ℹ️ Aucune tâche **Pull Messages** active trouvée. Lancez-en une avec `messages
pull`.
cli.cmd.messages.pull.no-chats-found:
Aucune conversation WhatsApp trouvée pour cette période.
cli.cmd.contacts.pull.option.progress:
Annonce la progression toutes les N contacts. Mettre 0 pour désactiver les
rapports.
cli.cmd.contacts.pull.option.lids:
Inclut les contacts LID WhatsApp (contacts anonymes) dans la synchronisation.
task.contacts.completed:
👤✅ Contacts terminés → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started:
⏳ Récupération des messages 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Envoyez "{{{prefix}}}messages status" pour plus de détails.
task.messages.progress:
'⏳ Messages {{{chat}}} : 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Messages {{{chat}}} pendant {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Messages **{{{chat}}}** sur la période ⏱️ **{{{period}}}**. ⚙️ État de la tâche : **{{state}}**.
🗨️ Messages :
- 🆕 Créés : **{{{progress.ok}}}**
- 🟢 Déjà existants : {{{progress.exists}}}
- ⚪ Ignorés : {{{progress.ignored}}}
- 🔴 Erreurs : {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Plus ancien : **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Plus récent : **{{{messages.last}}}**
{{/messages.last}}
💬 Conversations WhatsApp :
- 🆕 Nouveaux messages dans **{{{chats}}}** conversations
{{#options.resolveConversations}}
🗣️ Conversations :
- 🟢 Résolues : **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Erreurs : **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Détails :
[{{{details.text}}}]({{{details.url}}})
+335 -37
View File
@@ -1,4 +1,4 @@
# Hebrew translations for ChatWoot integration
# Hebrew translations for WhatsApp inbox integration
locale.name: '🇮🇱 עברית (Hebrew)'
# קוד הלוקאל הבסיסי - חייב להיות לוקאל תקין לפי BCP 47
@@ -6,23 +6,28 @@ locale.name: '🇮🇱 עברית (Hebrew)'
# WAHA משתמש בכך להצגת מספרים, מטבע ותאריך/שעה
locale.base: 'he-IL'
app.inbox.contact.name: 'אינטגרציית וואטסאפ (WAHA)'
app.inbox.contact.name: 'אינטגרציית וואטסאפ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "אפשרויות" של date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: he-IL
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Jerusalem'
Today: 'היום'
Yesterday: 'אתמול'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: לא ניתן להסיר הודעה מוואט
job.scheduled.error.header: המשימה המתוזמנת נכשלה בביצוע
message.from.whatsapp: |-
{{{text}}}
*📱 נשלח מוואטסאפ*
📱 *נשלח מוואטסאפ*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 נשלח דרך API*
📱 *נשלח דרך API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *הודעה זו נמחקה בוואטסאפ*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'רשימת שידור'
whatsapp.contact.status.name: 'סטטוס'
app.connected.message: |-
🔗 **מחובר: הפעלת וואטסאפ לתיבת הדואר הנכנס של ChatWoot**
🔗 **מחובר: הפעלת וואטסאפ לתיבת הדואר הנכנס**
✅ הפעלת וואטסאפ **{{{name}}}** חוברה בהצלחה לתיבת הדואר הנכנס של ChatWoot.
✅ הפעלת וואטסאפ **{{{name}}}** חוברה בהצלחה לתיבת דואר נכנס זו.
תיבת דואר נכנס זו מקושרת כעת ל**חשבון הוואטסאפ** שלך. אתה יכול לשלוח ולקבל הודעות ישירות מכאן — בדיוק כמו שימוש בוואטסאפ, אבל בתוך ChatWoot.
תיבת דואר נכנס זו מקושרת כעת ל**חשבון הוואטסאפ** שלך. אתה יכול לשלוח ולקבל הודעות ישירות מכאן — בדיוק כמו שימוש בוואטסאפ, אבל בתוך מרחב העבודה שלך.
אתה יכול גם לנהל את הפעלת הוואטסאפ המקושרת לתיבת דואר נכנס זו. כדי לראות פעולות זמינות, שלח **help** לצ'אט זה.
@@ -104,9 +109,9 @@ app.connected.message: |-
בוא נתחיל! 🚀
app.disconnected.message: |-
🔌 **מנותק: הפעלת וואטסאפ מתיבת הדואר הנכנס של ChatWoot**
🔌 **מנותק: הפעלת וואטסאפ מתיבת הדואר הנכנס**
❌ הפעלת וואטסאפ **{{{name}}}** נותקה מתיבת הדואר הנכנס של ChatWoot.
❌ הפעלת וואטסאפ **{{{name}}}** נותקה מתיבת דואר נכנס זו.
לא תוכל יותר לשלוח או לקבל הודעות מוואטסאפ.
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ ההגדרות עבור הפעלת וואטסאפ **{{{name}}}** עודכנו.
app.commands.list: |-
אתה יכול לנהל **אינטגרציית וואטסאפ** על ידי שליחת אחת מהפקודות הבאות:
cli.usage.arguments: |-
[פעולה]
cli.usage.command: |-
[פקודה]
cli.usage.options: |-
[אפשרויות]
cli.cmd.disabled: ⛔ **הפקודה `{{command}}` מושבתת**.
אנא פנה למנהל המערכת שלך.
שלח **help** כדי לקבל את רשימת הפקודות הזמינות.
cli.cmd.root.description: |-
נהל את **האינטגרציה של וואטסאפ** שלך!
cli.cmd.root.sub.session: |-
🖥️ **הפעלה**:
1. **status** - קבל את סטטוס ההפעלה הנוכחי.
2. **restart** - הפעל מחדש את ההפעלה - זה ינסה לחבר מחדש את חשבון הוואטסאפ שלך.
3. **start** - התחל את ההפעלה.
4. **stop** - עצור את ההפעלה.
5. **logout** - התנתק מההפעלה, כך שתוכל לסרוק קוד QR ולתקן את ההפעלה.
6. **qr** - קבל את קוד ה-QR עבור ההפעלה.
7. **screenshot** - קבל צילום מסך של ההפעלה (זמין רק במנוע **WEBJS**).
8. **help** - פקודות זמינות.
{{#commands.server}}
⚙️ שרת:
- **server status** - קבל מידע על גרסת השרת והסטטוס.
- **server reboot** - הפעל מחדש את השרת בצורה חלקה.
- **server reboot force** - אלץ הפעלה מחדש של השרת.
{{/commands.server}}
cli.cmd.root.sub.server: |-
🖥️ **שרת**:
שלח **פקודה** (לא תלוי רישיות) או **מספר פקודה** לשיחה זו.
cli.cmd.root.sub.sync: |-
🔄️ **סנכרון**:
app.commands.server.disabled: ⛔ **פקודות השרת מושבתות**.
cli.cmd.session.status.description: |-
קבל את סטטוס ההפעלה הנוכחי.
אנא פנו למנהל המערכת שלכם.
cli.cmd.session.restart.description: |-
אתחל את ההפעלה מחדש.
שלחו **help** כדי לקבל את רשימת הפקודות הזמינות.
cli.cmd.session.start.description: |-
התחל את ההפעלה.
cli.cmd.session.stop.description: |-
עצור את ההפעלה.
cli.cmd.session.logout.description: |-
התנתק מההפעלה כדי לסרוק קוד QR חדש ולהחזיר את החיבור.
cli.cmd.session.qr.description: |-
הצג את קוד ה-QR של ההפעלה.
cli.cmd.session.screenshot.description: |-
צלם צילום מסך של ההפעלה (זמין רק עם מנוע **WEBJS**).
cli.cmd.contacts.summary: |-
משיכת אנשי קשר של וואטסאפ.
cli.cmd.contacts.description: |-
👤 הרץ `contacts pull` כדי להתחיל סנכרון או `contacts status` כדי לבדוק את ההתקדמות.
cli.cmd.contacts.action.description: |-
השתמש ב-`start` כדי להתחיל למשוך אנשי קשר או ב-`status` כדי לבדוק את המשימה הפעילה.
cli.cmd.contacts.pull.description: |-
התחל למשוך אנשי קשר של וואטסאפ אל תיבת הדואר הנכנס.
cli.cmd.contacts.pull.option.batch: |-
מספר אנשי הקשר שנמשכים בכל אצווה.
cli.cmd.contacts.pull.option.delay-contact: |-
השהיה בין כל איש קשר כדי להימנע ממגבלות בעת משיכת תמונות הפרופיל.
cli.cmd.contacts.pull.option.delay-batch: |-
השהיה בין אצוות של אנשי קשר.
cli.cmd.contacts.pull.option.avatar: |-
בחר כיצד למשוך תמונות פרופיל: `skip`, `if-missing` או `update`.
cli.cmd.contacts.pull.option.groups: |-
כלול בסנכרון אנשי קשר של קבוצות WhatsApp.
cli.cmd.contacts.pull.option.no-attributes: |-
אל תמשוך מאפייני איש קשר אם הוא כבר קיים (מאיץ את התהליך).
cli.cmd.contacts.pull.already-running: |-
⏳ המשימה **Pull Contacts** כבר פועלת. המתן שתסתיים או עצור אותה לפני שמתחילים חדשה.
שלח `contacts status` לקבלת פרטים.
cli.cmd.options.job.attempts: |-
מספר ניסיונות החזרה לפני שסימנים את המשימה ככושלת.
cli.cmd.options.job.timeout: |-
משך הזמן המרבי שבו המשימה יכולה לפעול לפני ניסיון נוסף (תומך בערכים כגון `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
הצגת הסטטוס של משימת המשיכה הנוכחית.
cli.cmd.contacts.status.not-found: |-
ℹ️ לא נמצאה משימת **Pull Contacts** פעילה. התחל אחת עם `contacts pull`.
cli.cmd.server.description: |-
נהל את השרת.
cli.cmd.server.status.description: |-
הצג את גרסת השרת והסטטוס.
cli.cmd.server.reboot.description: |-
אתחל את השרת מחדש.
cli.cmd.server.reboot.option.force: |-
כפה אתחול מיידי.
cli.help.usage.title: |-
שימוש: שלח פקודה לשיחה בפורמט הבא:
cli.help.commands.defaultGroup: |-
**פקודות:**
cli.help.arguments.title: |-
**ארגומנטים:**
cli.help.options.defaultGroup: |-
**אפשרויות:**
cli.help.globalOptions.title: |-
**אפשרויות גלובליות:**
task.contacts.status: |-
👤 משיכת אנשי קשר: **{{state}}**
- ✅ הצלחות: {{{progress.ok}}} אנשי קשר
- ⚪ שדלגו עליהם: {{{progress.skipped}}} אנשי קשר
- ⚠️ שגיאות: {{{progress.errors}}} אנשי קשר
{{#job}}
⌛ ציר זמן:
- נכנסה לתור: {{{timestamp}}}
{{#processedOn}}
- התחלה: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- סיום: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
פרטים:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
אנשי קשר נמשכים...
task.contacts.progress: |-
משיכת אנשי קשר: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
שלח **help** כדי לקבל את רשימת הפקודות הזמינות.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ תיבת הדואר הנכנס של ChatWoot מקושרת ל**חשבון וואטסאפ** ומוכנה לשלוח ולקבל הודעות!
✅ תיבת דואר נכנס זו מקושרת ל**חשבון וואטסאפ** שלך ומוכנה לשלוח ולקבל הודעות!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **תיבת הדואר הנכנס של ChatWoot מקושרת לחשבון וואטסאפ אך אינה מוכנה לשלוח ולקבל הודעות!**
⚠️ **תיבת דואר נכנס זו מקושרת לחשבון וואטסאפ אך אינה מוכנה לשלוח ולקבל הודעות!**
אנא בדוק את סטטוס ההפעלה ונסה להפעיל אותה מחדש או התנתק וסרוק את קוד ה-QR שוב.
app.session.scan.qr.code: |-
⚠️ **סרוק את קוד ה-QR בטלפון שלך כדי לקשר את חשבון הוואטסאפ לתיבת הדואר הנכנס של ChatWoot!**
⚠️ **סרוק את קוד ה-QR בטלפון שלך כדי לקשר את חשבון הוואטסאפ לתיבת דואר נכנס זו!**
app.server.version.and.status: |-
**גרסה:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **סוג הודעה זה אינו נתמך ב-ChatWoot.**
⚠️ **סוג הודעה זה אינו נתמך בתיבה זו.**
📱 אנא פתח/י את **WhatsApp** כדי לצפות בזה.
פרטים: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **הודעת אלבום התקבלה**
{{#totalCount}}
התקבל אלבום עם **{{totalCount}}** קובץ/קבצי מדיה.
{{#expectedImageCount}}
📸 תמונות: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 סרטונים: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
קבצי המדיה יועברו בהודעות הבאות.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **מודעת פייסבוק**
@@ -352,3 +522,131 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **הודעת הלקוח:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: לא ידוע
ERROR: שגיאה
PENDING: נשלח
SERVER: שרת
DEVICE: מכשיר
READ: נקרא
PLAYED: הושמע
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **להודעה יש מדיה אך לא הצלחנו להוריד אותה**
📱 פתחו את **WhatsApp** כדי לצפות.
{{^content}}
פרטים: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **תור**:'
cli.cmd.messages.summary: משיכת הודעות WhatsApp.
cli.cmd.messages.description:
💬 הפעילו `messages pull` כדי להתחיל סנכרון הודעות או `messages status` למעקב
אחר ההתקדמות.
cli.cmd.messages.action.description:
'`pull` להתחיל משיכה או `status` לבדיקת המשימה הפעילה.'
cli.cmd.messages.pull.option.chat: הגבלת המשיכה למזהה שיחה מסוים ב-WhatsApp.
cli.cmd.messages.pull.option.force:
אילוץ תיבת הדואר הנכנס ליצור הודעות גם אם ההודעה כבר קיימת במיפוי השיחה.
cli.cmd.messages.pull.option.batch: כמות ההודעות הנמשכות בכל אצווה.
cli.cmd.messages.pull.option.progress:
דיווח על ההתקדמות כל N הודעות. ערך 0 מבטל את הדוחות.
cli.cmd.messages.pull.option.no-dm: דילוג על הודעות פרטיות בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.groups: הכללת קבוצות WhatsApp בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.channels: הכללת ערוצי WhatsApp בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.status:
הכללת סטטוסים של WhatsApp בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.broadcast: הכללת רשימות שידור בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.media:
הורדת קבצי מדיה (תמונות, סרטונים, קבצים) בעת משיכת ההיסטוריה.
cli.cmd.messages.pull.option.pause:
הכנסת משימת המשיכה לתור במצב מושהה ועצירת עיבוד הודעות WhatsApp חדשות עד
לסיום.
cli.cmd.messages.pull.option.resolve-conversations:
מסמן את השיחות כפתורות לאחר שכל הודעות ההיסטוריה עובדו.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ לא ניתן להשתמש ב-`--pause` כי הפקודה **queue** מושבתת בתצורה זו.
cli.cmd.queue.description: ניהול תורים.
cli.cmd.queue.summary: ביצוע פעולות על תורים.
cli.cmd.queue.action.description: בחרו פעולה.
cli.cmd.queue.status.description: הצגת מצב התור.
cli.cmd.queue.argument.name: 'שם התור: `inbox` או `whatsapp` (רשות).'
cli.cmd.queue.start.description: הפעלת תור לפי שם.
cli.cmd.queue.stop.description: עצירת תור לפי שם.
cli.cmd.queue.paused: ⚪🧩 התורים הושהו. שלחו `queue status` כדי לבדוק את המצב.
cli.cmd.queue.resumed: 🟢🧩 התורים חודשו. שלחו `queue status` כדי לבדוק את המצב.
cli.cmd.queue.status.result: >-
📊 מצב התור:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
מושהה{{/paused}}{{^paused}}🟢 פעיל{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
הגדרת משך הניסיון להוריד מדיה לכל הודעה.
cli.cmd.messages.pull.argument.end:
הגדרת נקודת הסיום של חלון המשיכה; למשל `2d` נעצר יומיים אחורה.
cli.cmd.messages.pull.argument.start:
הגדרת נקודת ההתחלה של החלון; למשל `1d` מתחיל יום אחורה (ברירת המחדל `0d`,
כלומר עכשיו).
cli.cmd.messages.pull.already-running: >-
⏳ המשימה **Pull Messages** כבר פועלת עבור **{{{chat}}}**. המתינו לסיום או
עצרו לפני התחלת משימה חדשה.
שלחו `messages status` לפרטים.
cli.cmd.messages.status.not-found:
ℹ️ לא נמצאה משימת **Pull Messages** פעילה. התחילו אחת עם `messages pull`.
cli.cmd.messages.pull.no-chats-found: לא נמצאו שיחות WhatsApp למשיכה בתקופה זו.
cli.cmd.contacts.pull.option.progress:
דיווח על התקדמות כל N אנשי קשר. ערך 0 מבטל את הדוחות.
cli.cmd.contacts.pull.option.lids:
הכללת אנשי קשר אנונימיים של WhatsApp (LID) בסנכרון.
task.contacts.completed:
👤✅ השלמת אנשי קשר → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ מושכים הודעות 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details: שלחו "{{{prefix}}}messages status" לעוד פרטים.
task.messages.progress:
'⏳ הודעות {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ הודעות {{{chat}}} במהלך {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
הודעות **{{{chat}}}** בתקופה ⏱️ **{{{period}}}**. ⚙️ מצב המשימה: **{{state}}**.
🗨️ הודעות:
- 🆕 נוצרו: **{{{progress.ok}}}**
- 🟢 קיימות כבר: {{{progress.exists}}}
- ⚪ הוזנחו: {{{progress.ignored}}}
- 🔴 שגיאות: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ ישן ביותר: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ חדש ביותר: **{{{messages.last}}}**
{{/messages.last}}
💬 שיחות WhatsApp:
- 🆕 הודעות חדשות ב-**{{{chats}}}** שיחות
{{#options.resolveConversations}}
🗣️ שיחות:
- 🟢 נפתרו: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 שגיאות: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
פרטים:
[{{{details.text}}}]({{{details.url}}})
+349 -38
View File
@@ -1,4 +1,4 @@
# Hindi translations for ChatWoot integration
# Hindi translations for WhatsApp inbox integration
locale.name: '🇮🇳 हिंदी (Hindi)'
# बेस लोकैल कोड - यह एक मान्य BCP 47 लोकैल होना चाहिए
@@ -6,23 +6,28 @@ locale.name: '🇮🇳 हिंदी (Hindi)'
# WAHA इसे संख्याएँ, मुद्रा और दिनांक/समय प्रदर्शित करने के लिए उपयोग करता है।
locale.base: 'hi-IN'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन (WAHA)'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString की "options"
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: hi-IN
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: true
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Kolkata'
Today: 'आज'
Yesterday: 'कल'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: व्हाट्सएप से संदे
job.scheduled.error.header: निर्धारित कार्य निष्पादित करने में विफल रहा
message.from.whatsapp: |-
{{{text}}}
*📱 व्हाट्सएप से भेजा गया*
📱 *व्हाट्सएप से भेजा गया*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 API के माध्यम से भेजा गया*
📱 *API के माध्यम से भेजा गया*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *यह संदेश व्हाट्सएप में हटा दिया गया था*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'प्रसारण सूची'
whatsapp.contact.status.name: 'स्थिति'
app.connected.message: |-
🔗 **कनेक्टेड: व्हाट्सएप सत्र से ChatWoot इनबॉक्स**
🔗 **कनेक्टेड: व्हाट्सएप सत्र से इनबॉक्स**
✅ व्हाट्सएप सत्र **{{{name}}}** सफलतापूर्वक इस ChatWoot इनबॉक्स से जुड़ गया है।
✅ व्हाट्सएप सत्र **{{{name}}}** सफलतापूर्वक इस इनबॉक्स से जुड़ गया है।
यह इनबॉक्स अब आपके **व्हाट्सएप खाते** से जुड़ा हुआ है। आप यहां से सीधे संदेश भेज और प्राप्त कर सकते हैं — बिल्कुल व्हाट्सएप का उपयोग करने जैसा, लेकिन ChatWoot के अंदर।
यह इनबॉक्स अब आपके **व्हाट्सएप खाते** से जुड़ा हुआ है। आप यहां से सीधे संदेश भेज और प्राप्त कर सकते हैं — बिल्कुल व्हाट्सएप का उपयोग करने जैसा, लेकिन आपके कार्यक्षेत्र के भीतर।
आप इस इनबॉक्स से जुड़े व्हाट्सएप सत्र को भी प्रबंधित कर सकते हैं। उपलब्ध कार्यों को देखने के लिए, इस चैट पर **help** भेजें।
@@ -104,9 +109,9 @@ app.connected.message: |-
चलिए शुरू करते हैं! 🚀
app.disconnected.message: |-
🔌 **डिस्कनेक्टेड: व्हाट्सएप सत्र से ChatWoot इनबॉक्स**
🔌 **डिस्कनेक्टेड: व्हाट्सएप सत्र से इनबॉक्स**
❌ व्हाट्सएप सत्र **{{{name}}}** इस ChatWoot इनबॉक्स से डिस्कनेक्ट हो गया है।
❌ व्हाट्सएप सत्र **{{{name}}}** इस इनबॉक्स से डिस्कनेक्ट हो गया है।
आप अब व्हाट्सएप से संदेश भेज या प्राप्त नहीं कर पाएंगे।
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ व्हाट्सएप सत्र **{{{name}}}** की सेटिंग्स अपडेट की गई हैं।
app.commands.list: |-
आप निम्न कमांड में से एक भेजकर **व्हाट्सएप इंटीग्रेशन** का प्रबंधन कर सकते हैं:
cli.usage.arguments: |-
[क्रिया]
🖥️ **सेशन**:
1. **status** - वर्तमान सत्र स्थिति प्राप्त करें।
2. **restart** - सत्र को पुनरारंभ करें - यह आपके व्हाट्सएप खाते को फिर से कनेक्ट करने का प्रयास करेगा।
3. **start** - सत्र शुरू करें।
4. **stop** - सत्र बंद करें।
5. **logout** - सत्र से लॉग आउट करें, ताकि आप QR कोड स्कैन कर सकें और सत्र को मरम्मत कर सकें।
6. **qr** - सत्र के लिए QR कोड प्राप्त करें।
7. **screenshot** - सत्र का स्क्रीनशॉट प्राप्त करें (केवल **WEBJS** इंजन में उपलब्ध)।
8. **help** - उपलब्ध कमांड।
cli.usage.command: |-
[कमांड]
{{#commands.server}}
⚙️ सर्वर:
- **server status** - सर्वर संस्करण और स्थिति जानकारी प्राप्त करें।
- **server reboot** - सर्वर को सुचारू रूप से पुनरारंभ करें।
- **server reboot force** - सर्वर को जबरदस्ती पुनरारंभ करें।
{{/commands.server}}
cli.usage.options: |-
[विकल्प]
इस वार्तालाप में **कमांड** (केस इनसेंसिटिव) या **कमांड नंबर** भेजें।
cli.cmd.disabled: ⛔ **`{{command}}` कमांड अक्षम है**.
app.commands.server.disabled: ⛔ **सर्वर कमांड अक्षम हैं**।
कृपया अपने प्रशासक से संपर्क करें।
कृपया अपने व्यवस्थापक से संपर्क करें।
उपलब्ध कमांड की सूची प्राप्त करने के लिए **help** भेजें।
उपलब्ध कमांड की सूची पाने के लिए **help** भेजें।
cli.cmd.root.description: |-
अपने **व्हाट्सएप इंटीग्रेशन** का प्रबंधन करें!
cli.cmd.root.sub.session: |-
🖥️ **सत्र**:
cli.cmd.root.sub.server: |-
🖥️ **सर्वर**:
cli.cmd.root.sub.sync: |-
🔄️ **सिंक**:
cli.cmd.session.status.description: |-
वर्तमान सत्र की स्थिति प्राप्त करें।
cli.cmd.session.restart.description: |-
सत्र को पुनरारंभ करें।
cli.cmd.session.start.description: |-
सत्र प्रारंभ करें।
cli.cmd.session.stop.description: |-
सत्र रोकें।
cli.cmd.session.logout.description: |-
नया QR कोड स्कैन करके कनेक्शन बहाल करने के लिए सत्र से लॉग आउट करें।
cli.cmd.session.qr.description: |-
सत्र का QR कोड दिखाएं।
cli.cmd.session.screenshot.description: |-
सत्र का स्क्रीनशॉट लें (केवल **WEBJS** इंजन के साथ उपलब्ध)।
cli.cmd.contacts.summary: |-
व्हाट्सएप संपर्क प्राप्त करें।
cli.cmd.contacts.description: |-
👤 सिंक शुरू करने के लिए `contacts pull` चलाएँ या प्रगति देखने के लिए `contacts status` चलाएँ।
cli.cmd.contacts.action.description: |-
`start` से संपर्क खींचना शुरू करें या `status` से सक्रिय कार्य जांचें।
cli.cmd.contacts.pull.description: |-
व्हाट्सएप संपर्कों को इस इनबॉक्स में लाना शुरू करें।
cli.cmd.contacts.pull.option.batch: |-
प्रत्येक बैच में खींचे जाने वाले संपर्कों की संख्या।
cli.cmd.contacts.pull.option.delay-contact: |-
हर संपर्क के बीच देरी ताकि अवतार खींचते समय रेट लिमिट से बचा जा सके।
cli.cmd.contacts.pull.option.delay-batch: |-
संपर्क बैचों के बीच देरी।
cli.cmd.contacts.pull.option.avatar: |-
संपर्क अवतार कैसे खींचें चुनें: `skip`, `if-missing`, या `update`।
cli.cmd.contacts.pull.option.groups: |-
सिंक में WhatsApp समूह संपर्क शामिल करें।
cli.cmd.contacts.pull.option.no-attributes: |-
यदि संपर्क पहले से मौजूद है तो उसके गुण न खींचें (इससे प्रक्रिया तेज होती है)।
cli.cmd.contacts.pull.already-running: |-
⏳ **Pull Contacts** कार्य पहले से ही चल रहा है। इसे समाप्त होने दें या नया शुरू करने से पहले रोकें।
विवरण के लिए `contacts status` भेजें।
cli.cmd.options.job.attempts: |-
कार्य को विफल मानने से पहले पुनः प्रयासों की संख्या।
cli.cmd.options.job.timeout: |-
कार्य को पुनः कोशिश करने से पहले अधिकतम चलने का समय (जैसे `30s`, `5m`, `1h`)।
cli.cmd.contacts.status.description: |-
वर्तमान खींचने वाले कार्य की स्थिति दिखाएँ।
cli.cmd.contacts.status.not-found: |-
ℹ️ कोई सक्रिय **Pull Contacts** कार्य नहीं मिला। `contacts pull` से एक शुरू करें।
cli.cmd.server.description: |-
सर्वर का प्रबंधन करें।
cli.cmd.server.status.description: |-
सर्वर का संस्करण और स्थिति दिखाएं।
cli.cmd.server.reboot.description: |-
सर्वर को पुनरारंभ करें।
cli.cmd.server.reboot.option.force: |-
तुरंत पुनरारंभ को मजबूर करें।
cli.help.usage.title: |-
उपयोग: नीचे दिए गए प्रारूप में बातचीत में कमांड भेजें:
cli.help.commands.defaultGroup: |-
**कमांड:**
cli.help.arguments.title: |-
**आर्ग्युमेंट्स:**
cli.help.options.defaultGroup: |-
**विकल्प:**
cli.help.globalOptions.title: |-
**ग्लोबल विकल्प:**
task.contacts.status: |-
👤 संपर्क खींचना: **{{state}}**
- ✅ सफल: {{{progress.ok}}} संपर्क
- ⚪ छोड़े गए: {{{progress.skipped}}} संपर्क
- ⚠️ त्रुटियाँ: {{{progress.errors}}} संपर्क
{{#job}}
⌛ समयरेखा:
- कतार में: {{{timestamp}}}
{{#processedOn}}
- शुरू हुआ: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- समाप्त: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
विवरण:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
संपर्क खींचे जा रहे हैं...
task.contacts.progress: |-
संपर्क खींचना: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
उपलब्ध कमांड की सूची प्राप्त करने के लिए **help** भेजें।
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ यह ChatWoot इनबॉक्स **व्हाट्सएप खाते** से जुड़ा हुआ है और संदेश भेजने और प्राप्त करने के लिए तैयार है!
✅ यह इनबॉक्स आपके **व्हाट्सएप खाते** से जुड़ा हुआ है और संदेश भेजने और प्राप्त करने के लिए तैयार है!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **यह ChatWoot इनबॉक्स व्हाट्सएप खाते से जुड़ा हुआ है लेकिन संदेश भेजने और प्राप्त करने के लिए तैयार नहीं है!**
⚠️ **यह इनबॉक्स व्हाट्सएप खाते से जुड़ा हुआ है लेकिन संदेश भेजने और प्राप्त करने के लिए तैयार नहीं है!**
कृपया सत्र की स्थिति जांचें और इसे पुनरारंभ करने का प्रयास करें या लॉग आउट करें और QR कोड फिर से स्कैन करें।
app.session.scan.qr.code: |-
⚠️ **इस ChatWoot इनबॉक्स के साथ व्हाट्सएप खाता जोड़ने के लिए अपने फोन पर QR कोड स्कैन करें!**
⚠️ **इस इनबॉक्स के साथ व्हाट्सएप खाता जोड़ने के लिए अपने फोन पर QR कोड स्कैन करें!**
app.server.version.and.status: |-
**संस्करण:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **इस प्रकार का संदेश ChatWoot में समर्थित नहीं है।**
⚠️ **इस प्रकार का संदेश इस इनबॉक्स में समर्थित नहीं है।**
📱 इसे देखने के लिए कृपया **WhatsApp** खोलें।
विवरण: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **एल्बम संदेश प्राप्त हुआ**
{{#totalCount}}
**{{totalCount}}** मीडिया फ़ाइल(ओं) वाला एक एल्बम प्राप्त हुआ।
{{#expectedImageCount}}
📸 छवियाँ: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 वीडियो: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
मीडिया फ़ाइलें अगले संदेशों में वितरित की जाएंगी।
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **फेसबुक विज्ञापन**
@@ -352,3 +522,144 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **ग्राहक संदेश:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: अज्ञात
ERROR: त्रुटि
PENDING: भेजा जा रहा है
SERVER: सर्वर
DEVICE: डिवाइस
READ: पढ़ा गया
PLAYED: चलाया गया
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **संदेश में मीडिया है लेकिन हम इसे डाउनलोड नहीं कर सके**
📱 कृपया इसे देखने के लिए **WhatsApp** खोलें।
{{^content}}
विवरण: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **क्यू**:'
cli.cmd.messages.summary: WhatsApp संदेश खींचें।
cli.cmd.messages.description:
💬 संदेश सिंक शुरू करने के लिए `messages pull` चलाएँ या प्रगति देखने के लिए
`messages status` चलाएँ।
cli.cmd.messages.action.description:
संदेश खींचने के लिए `pull` या सक्रिय कार्य देखने के लिए `status` का उपयोग
करें।
cli.cmd.messages.pull.option.chat:
खींचने को किसी विशिष्ट WhatsApp चैट आईडी तक सीमित करें।
cli.cmd.messages.pull.option.force:
अगर WhatsApp संदेश बातचीत मैपिंग में पहले से मौजूद है तब भी इनबॉक्स को संदेश
बनाने के लिए बाध्य करें।
cli.cmd.messages.pull.option.batch:
प्रत्येक बैच में खींचे जाने वाले संदेशों की संख्या।
cli.cmd.messages.pull.option.progress:
हर N संदेश पर प्रगति रिपोर्ट करें। रिपोर्ट बंद करने के लिए 0 सेट करें।
cli.cmd.messages.pull.option.no-dm: इतिहास खींचते समय डायरेक्ट संदेश छोड़ें।
cli.cmd.messages.pull.option.groups: इतिहास खींचते समय WhatsApp समूह शामिल करें।
cli.cmd.messages.pull.option.channels:
इतिहास खींचते समय WhatsApp चैनल शामिल करें।
cli.cmd.messages.pull.option.status:
इतिहास खींचते समय WhatsApp स्टेटस अपडेट शामिल करें।
cli.cmd.messages.pull.option.broadcast:
इतिहास खींचते समय प्रसारण सूचियाँ शामिल करें।
cli.cmd.messages.pull.option.media:
इतिहास खींचते समय मीडिया (छवियाँ, वीडियो, फाइलें) डाउनलोड करें।
cli.cmd.messages.pull.option.pause:
संदेश खींचने के कार्य को रोके गए अवस्था में कतार में डालें और पूरा होने तक नए
WhatsApp संदेशों की प्रोसेसिंग रोकें।
cli.cmd.messages.pull.option.resolve-conversations:
सभी इतिहास संदेश संसाधित होने के बाद बातचीत को हल किया हुआ चिह्नित करता है।
cli.cmd.messages.pull.error.pause-no-queue:
⛔ `--pause` का उपयोग नहीं किया जा सकता क्योंकि इस कॉन्फ़िगरेशन में **queue**
कमांड अक्षम है।
cli.cmd.queue.description: क्यू प्रबंधित करें।
cli.cmd.queue.summary: क्यू पर क्रियाएँ चलाएँ।
cli.cmd.queue.action.description: एक क्रिया चुनें।
cli.cmd.queue.status.description: किसी क्यू की स्थिति दिखाएँ।
cli.cmd.queue.argument.name: 'क्यू का नाम: `inbox` या `whatsapp` (वैकल्पिक)।'
cli.cmd.queue.start.description: नाम से क्यू शुरू करें।
cli.cmd.queue.stop.description: नाम से क्यू बंद करें।
cli.cmd.queue.paused:
⚪🧩 क्यू रोक दी गई हैं। स्थिति देखने के लिए `queue status` भेजें।
cli.cmd.queue.resumed:
🟢🧩 क्यू फिर से शुरू हो गई हैं। स्थिति देखने के लिए `queue status` भेजें।
cli.cmd.queue.status.result: >-
📊 क्यू की स्थिति:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ रुकी
हुई{{/paused}}{{^paused}}🟢 सक्रिय{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
प्रत्येक संदेश के लिए मीडिया डाउनलोड करने की अवधि निर्धारित करें।
cli.cmd.messages.pull.argument.end:
खींचने की विंडो कहाँ समाप्त होगी, तय करें; उदाहरण के लिए `2d` दो दिन पहले
रुकता है।
cli.cmd.messages.pull.argument.start:
खींचने की विंडो कहाँ से शुरू होगी, तय करें; उदाहरण के लिए `1d` एक दिन पहले से
शुरू होता है (डिफ़ॉल्ट `0d`, यानी अभी)।
cli.cmd.messages.pull.already-running: >-
⏳ **Pull Messages** कार्य पहले से ही **{{{chat}}}** के लिए चल रहा है। समाप्त
होने तक प्रतीक्षा करें या नया शुरू करने से पहले इसे रोकें।
विवरण के लिए `messages status` भेजें।
cli.cmd.messages.status.not-found:
ℹ️ कोई सक्रिय **Pull Messages** कार्य नहीं मिला। `messages pull` से एक शुरू
करें।
cli.cmd.messages.pull.no-chats-found:
निर्धारित अवधि के लिए कोई WhatsApp चैट नहीं मिली।
cli.cmd.contacts.pull.option.progress:
हर N संपर्क पर प्रगति रिपोर्ट करें। रिपोर्ट बंद करने के लिए 0 सेट करें।
cli.cmd.contacts.pull.option.lids:
सिंक में WhatsApp LID (अनाम) संपर्क शामिल करें।
task.contacts.completed:
👤✅ संपर्क पूरे → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started:
⏳ संदेश खींचे जा रहे हैं 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details: और जानकारी के लिए "{{{prefix}}}messages status" भेजें।
task.messages.progress:
'⏳ संदेश {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ संदेश {{{chat}}} में {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
**{{{chat}}}** के संदेश ⏱️ **{{{period}}}** अवधि में। ⚙️ कार्य की स्थिति: **{{state}}**.
🗨️ संदेश:
- 🆕 बनाए गए: **{{{progress.ok}}}**
- 🟢 पहले से मौजूद: {{{progress.exists}}}
- ⚪ अनदेखा: {{{progress.ignored}}}
- 🔴 त्रुटियाँ: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ सबसे पुराना: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ नवीनतम: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp चैट:
- 🆕 **{{{chats}}}** चैट में नए संदेश
{{#options.resolveConversations}}
🗣️ बातचीत:
- 🟢 हल हुईं: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 त्रुटियाँ: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
विवरण:
[{{{details.text}}}]({{{details.url}}})
+349 -38
View File
@@ -1,4 +1,4 @@
# Indonesian translations for ChatWoot integration
# Indonesian translations for WhatsApp inbox integration
locale.name: '🇮🇩 Bahasa Indonesia (Indonesian)'
# Kode locale dasar - harus berupa locale BCP 47 yang valid
@@ -6,23 +6,28 @@ locale.name: '🇮🇩 Bahasa Indonesia (Indonesian)'
# WAHA menggunakannya untuk format angka, mata uang, dan tanggal/waktu
locale.base: 'id-ID'
app.inbox.contact.name: 'Integrasi WhatsApp (WAHA)'
app.inbox.contact.name: 'Integrasi WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opsi" dari date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: id-ID
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Jakarta'
Today: 'Hari ini'
Yesterday: 'Kemarin'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: Tidak dapat menghapus pesan dari WhatsApp
job.scheduled.error.header: Tugas terjadwal gagal dijalankan
message.from.whatsapp: |-
{{{text}}}
*📱 Dikirim dari WhatsApp*
📱 *Dikirim dari WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Dikirim melalui API*
📱 *Dikirim melalui API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Pesan ini telah dihapus di WhatsApp*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Daftar Siaran'
whatsapp.contact.status.name: 'Status'
app.connected.message: |-
🔗 **TERHUBUNG: Sesi WhatsApp ke Kotak Masuk ChatWoot**
🔗 **TERHUBUNG: Sesi WhatsApp ke kotak masuk**
✅ Sesi WhatsApp **{{{name}}}** telah berhasil terhubung ke Kotak Masuk ChatWoot ini.
✅ Sesi WhatsApp **{{{name}}}** telah berhasil terhubung ke kotak masuk ini.
Kotak masuk ini sekarang terhubung ke **Akun WhatsApp** Anda. Anda dapat mengirim dan menerima pesan langsung dari sini — seperti menggunakan WhatsApp, tetapi di dalam ChatWoot.
Kotak masuk ini sekarang terhubung ke **Akun WhatsApp** Anda. Anda dapat mengirim dan menerima pesan langsung dari sini — seperti menggunakan WhatsApp, tetapi di dalam ruang kerja Anda.
Anda juga dapat mengelola sesi WhatsApp yang terhubung ke kotak masuk ini. Untuk melihat tindakan yang tersedia, kirim **help** ke obrolan ini.
@@ -104,9 +109,9 @@ app.connected.message: |-
Mari kita mulai! 🚀
app.disconnected.message: |-
🔌 **TERPUTUS: Sesi WhatsApp dari Kotak Masuk ChatWoot**
🔌 **TERPUTUS: Sesi WhatsApp dari kotak masuk**
❌ Sesi WhatsApp **{{{name}}}** telah terputus dari Kotak Masuk ChatWoot ini.
❌ Sesi WhatsApp **{{{name}}}** telah terputus dari kotak masuk ini.
Anda tidak akan dapat lagi mengirim atau menerima pesan dari WhatsApp.
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ Pengaturan untuk sesi WhatsApp **{{{name}}}** telah diperbarui.
app.commands.list: |-
Anda dapat mengelola **Integrasi WhatsApp** dengan mengirimkan salah satu perintah berikut:
cli.usage.arguments: |-
[aksi]
🖥️ **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.
4. **stop** - hentikan sesi.
5. **logout** - keluar dari sesi, sehingga Anda dapat memindai kode QR dan memperbaiki sesi.
6. **qr** - dapatkan kode QR untuk sesi.
7. **screenshot** - dapatkan tangkapan layar sesi (hanya tersedia di mesin **WEBJS**).
8. **help** - perintah yang tersedia.
cli.usage.command: |-
[perintah]
{{#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}}
cli.usage.options: |-
[opsi]
Kirim **perintah** (tidak peka huruf besar/kecil) atau **nomor perintah** ke percakapan ini.
app.commands.server.disabled: ⛔ **Perintah server dinonaktifkan**.
cli.cmd.disabled: ⛔ **Perintah `{{command}}` dinonaktifkan**.
Silakan hubungi administrator Anda.
Kirim **help** untuk melihat daftar perintah yang tersedia.
Kirim **help** untuk mendapatkan daftar perintah yang tersedia.
cli.cmd.root.description: |-
Kelola **Integrasi WhatsApp** Anda!
cli.cmd.root.sub.session: |-
🖥️ **Sesi**:
cli.cmd.root.sub.server: |-
🖥️ **Server**:
cli.cmd.root.sub.sync: |-
🔄️ **Sinkronisasi**:
cli.cmd.session.status.description: |-
Dapatkan status sesi saat ini.
cli.cmd.session.restart.description: |-
Mulai ulang sesi.
cli.cmd.session.start.description: |-
Mulai sesi.
cli.cmd.session.stop.description: |-
Hentikan sesi.
cli.cmd.session.logout.description: |-
Keluar dari sesi untuk memindai kode QR baru dan memulihkan koneksi.
cli.cmd.session.qr.description: |-
Tampilkan kode QR sesi.
cli.cmd.session.screenshot.description: |-
Ambil tangkapan layar sesi (hanya tersedia dengan mesin **WEBJS**).
cli.cmd.contacts.summary: |-
Tarik kontak WhatsApp.
cli.cmd.contacts.description: |-
👤 Jalankan `contacts pull` untuk memulai sinkronisasi atau `contacts status` untuk melihat progres.
cli.cmd.contacts.action.description: |-
Gunakan `start` untuk mulai menarik kontak atau `status` untuk memeriksa pekerjaan aktif.
cli.cmd.contacts.pull.description: |-
Mulai menarik kontak WhatsApp ke kotak masuk ini.
cli.cmd.contacts.pull.option.batch: |-
Jumlah kontak yang ditarik di setiap batch.
cli.cmd.contacts.pull.option.delay-contact: |-
Jeda antar tiap kontak untuk menghindari pembatasan saat mengambil avatar.
cli.cmd.contacts.pull.option.delay-batch: |-
Jeda antar batch kontak.
cli.cmd.contacts.pull.option.avatar: |-
Pilih cara mengambil avatar kontak: `skip`, `if-missing`, atau `update`.
cli.cmd.contacts.pull.option.groups: |-
Sertakan kontak grup WhatsApp dalam sinkronisasi.
cli.cmd.contacts.pull.option.no-attributes: |-
Jangan ambil atribut kontak jika kontak sudah ada (mempercepat proses).
cli.cmd.contacts.pull.already-running: |-
⏳ Pekerjaan **Pull Contacts** sudah berjalan. Tunggu hingga selesai atau hentikan sebelum memulai yang baru.
Kirim `contacts status` untuk detail.
cli.cmd.options.job.attempts: |-
Jumlah percobaan ulang sebelum pekerjaan dianggap gagal.
cli.cmd.options.job.timeout: |-
Durasi maksimum pekerjaan sebelum dicoba ulang (mendukung nilai seperti `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Tampilkan status pekerjaan penarikan saat ini.
cli.cmd.contacts.status.not-found: |-
ℹ️ Tidak ada pekerjaan **Pull Contacts** aktif. Mulai satu dengan `contacts pull`.
cli.cmd.server.description: |-
Kelola server.
cli.cmd.server.status.description: |-
Tampilkan versi dan status server.
cli.cmd.server.reboot.description: |-
Mulai ulang server.
cli.cmd.server.reboot.option.force: |-
Paksa reboot segera.
cli.help.usage.title: |-
Penggunaan: kirim perintah ke percakapan dalam format berikut:
cli.help.commands.defaultGroup: |-
**Perintah:**
cli.help.arguments.title: |-
**Argumen:**
cli.help.options.defaultGroup: |-
**Opsi:**
cli.help.globalOptions.title: |-
**Opsi global:**
task.contacts.status: |-
👤 Penarikan kontak: **{{state}}**
- ✅ Berhasil: {{{progress.ok}}} kontak
- ⚪ Dilewati: {{{progress.skipped}}} kontak
- ⚠️ Kesalahan: {{{progress.errors}}} kontak
{{#job}}
⌛ Linimasa:
- Masuk antrean: {{{timestamp}}}
{{#processedOn}}
- Mulai: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Selesai: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Detail:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Menarik kontak...
task.contacts.progress: |-
Penarikan kontak: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Kirim **help** untuk mendapatkan daftar perintah yang tersedia.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Kotak Masuk ChatWoot ini terhubung ke **Akun WhatsApp** dan siap untuk mengirim dan menerima pesan!
✅ Kotak masuk ini terhubung ke **Akun WhatsApp** Anda dan siap untuk mengirim dan menerima pesan!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **Kotak Masuk ChatWoot ini terhubung ke Akun WhatsApp tetapi TIDAK siap untuk mengirim dan menerima pesan!**
⚠️ **Kotak masuk 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: |-
⚠️ **Pindai kode QR di ponsel Anda untuk menghubungkan Akun WhatsApp dengan Kotak Masuk ChatWoot ini!**
⚠️ **Pindai kode QR di ponsel Anda untuk menghubungkan Akun WhatsApp dengan kotak masuk ini!**
app.server.version.and.status: |-
**Versi:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Jenis pesan ini tidak didukung di ChatWoot.**
⚠️ **Jenis pesan ini tidak didukung di kotak masuk ini.**
📱 Silakan buka **WhatsApp** untuk melihatnya.
Detail: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Pesan Album Diterima**
{{#totalCount}}
Menerima album dengan **{{totalCount}}** file media.
{{#expectedImageCount}}
📸 Gambar: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Video: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
File media akan dikirimkan dalam pesan berikutnya.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **IKLAN FACEBOOK**
@@ -352,3 +522,144 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Pesan Pelanggan:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Tidak diketahui
ERROR: Kesalahan
PENDING: Mengirim
SERVER: Server
DEVICE: Perangkat
READ: Dibaca
PLAYED: Diputar
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **Pesan memiliki media, tetapi kami tidak dapat mengunduhnya**
📱 Buka **WhatsApp** untuk melihatnya.
{{^content}}
Detail: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Antrian**:'
cli.cmd.messages.summary: Tarik pesan WhatsApp.
cli.cmd.messages.description:
💬 Jalankan `messages pull` untuk memulai sinkronisasi pesan atau `messages
status` untuk memantau progres.
cli.cmd.messages.action.description:
'`pull` untuk memulai penarikan pesan atau `status` untuk memeriksa pekerjaan
yang berjalan.'
cli.cmd.messages.pull.option.chat:
Batasi penarikan pada ID chat WhatsApp tertentu.
cli.cmd.messages.pull.option.force:
Paksa kotak masuk membuat pesan meskipun pesan WhatsApp sudah ada di pemetaan
percakapan.
cli.cmd.messages.pull.option.batch: Jumlah pesan yang ditarik di setiap batch.
cli.cmd.messages.pull.option.progress:
Laporkan progres setiap N pesan. Setel ke 0 untuk menonaktifkan laporan.
cli.cmd.messages.pull.option.no-dm: Lewati pesan langsung saat menarik riwayat.
cli.cmd.messages.pull.option.groups:
Sertakan grup WhatsApp saat menarik riwayat.
cli.cmd.messages.pull.option.channels:
Sertakan Channel WhatsApp saat menarik riwayat.
cli.cmd.messages.pull.option.status:
Sertakan pembaruan status WhatsApp saat menarik riwayat.
cli.cmd.messages.pull.option.broadcast:
Sertakan daftar siaran saat menarik riwayat.
cli.cmd.messages.pull.option.media:
Unduh lampiran media (gambar, video, file) saat menarik riwayat.
cli.cmd.messages.pull.option.pause:
Masukkan pekerjaan penarikan pesan ke antrian dalam keadaan jeda dan hentikan
pemrosesan pesan WhatsApp baru sampai selesai.
cli.cmd.messages.pull.option.resolve-conversations:
Menandai percakapan sebagai selesai setelah semua pesan riwayat diproses.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ Tidak dapat menggunakan `--pause` karena perintah **queue** dinonaktifkan
pada konfigurasi ini.
cli.cmd.queue.description: Kelola antrian.
cli.cmd.queue.summary: Jalankan tindakan pada antrian.
cli.cmd.queue.action.description: Pilih sebuah tindakan.
cli.cmd.queue.status.description: Tampilkan status sebuah antrian.
cli.cmd.queue.argument.name: 'Nama antrian: `inbox` atau `whatsapp` (opsional).'
cli.cmd.queue.start.description: Mulai antrian berdasarkan nama.
cli.cmd.queue.stop.description: Hentikan antrian berdasarkan nama.
cli.cmd.queue.paused:
⚪🧩 Antrian dijeda. Kirim `queue status` untuk memeriksa status.
cli.cmd.queue.resumed:
🟢🧩 Antrian dilanjutkan. Kirim `queue status` untuk memeriksa status.
cli.cmd.queue.status.result: >-
📊 Status antrian:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
Dijeda{{/paused}}{{^paused}}🟢 Berjalan{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Tetapkan berapa lama mencoba mengunduh media untuk setiap pesan.
cli.cmd.messages.pull.argument.end:
Tetapkan titik akhir jendela penarikan; misalnya `2d` berhenti dua hari yang
lalu.
cli.cmd.messages.pull.argument.start:
Tetapkan titik awal jendela penarikan; misalnya `1d` dimulai satu hari yang
lalu (default `0d`, yaitu sekarang).
cli.cmd.messages.pull.already-running: >-
⏳ Pekerjaan **Pull Messages** sudah berjalan untuk **{{{chat}}}**. Tunggu
hingga selesai atau hentikan sebelum memulai yang baru.
Kirim `messages status` untuk detail.
cli.cmd.messages.status.not-found:
ℹ️ Tidak ada pekerjaan **Pull Messages** aktif yang ditemukan. Mulai dengan
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
Tidak ada chat WhatsApp yang ditemukan untuk periode tersebut.
cli.cmd.contacts.pull.option.progress:
Laporkan progres setiap N kontak. Setel ke 0 untuk menonaktifkan laporan.
cli.cmd.contacts.pull.option.lids:
Sertakan kontak WhatsApp LID (kontak anonim) dalam sinkronisasi.
task.contacts.completed:
👤✅ Kontak selesai → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ Menarik pesan 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Kirim "{{{prefix}}}messages status" untuk melihat rincian.
task.messages.progress:
'⏳ Pesan {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Pesan {{{chat}}} selama {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Pesan **{{{chat}}}** dalam periode ⏱️ **{{{period}}}**. ⚙️ Status pekerjaan: **{{state}}**.
🗨️ Pesan:
- 🆕 Dibuat: **{{{progress.ok}}}**
- 🟢 Sudah ada: {{{progress.exists}}}
- ⚪ Diabaikan: {{{progress.ignored}}}
- 🔴 Kesalahan: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Paling awal: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Terbaru: **{{{messages.last}}}**
{{/messages.last}}
💬 Chat WhatsApp:
- 🆕 Pesan baru di **{{{chats}}}** chat
{{#options.resolveConversations}}
🗣️ Percakapan:
- 🟢 Selesai: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Kesalahan: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Detail:
[{{{details.text}}}]({{{details.url}}})
+348 -38
View File
@@ -1,4 +1,4 @@
# Punjabi translations for ChatWoot integration
# Punjabi translations for WhatsApp inbox integration
locale.name: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
# ਅਧਾਰਭੂਤ ਲੋਕੇਲ ਕੋਡ - ਇਹ ਇੱਕ ਵੈਧ BCP 47 ਲੋਕੇਲ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ
@@ -6,23 +6,28 @@ locale.name: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
# WAHA ਇਸਨੂੰ ਅੰਕ, ਕਰੰਸੀ ਅਤੇ ਤਾਰੀਖ/ਸਮੇਂ ਦੀ ਨਮਾਇਸ਼ ਲਈ ਵਰਤਦਾ ਹੈ
locale.base: 'pa-PK'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (WAHA)'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString ਦੀਆਂ "options"
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: pa-PK
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: true
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Asia/Karachi'
Today: 'ਅੱਜ'
Yesterday: 'ਕੱਲ੍ਹ'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹਾ
job.scheduled.error.header: ਨਿਰਧਾਰਿਤ ਕੰਮ ਚਲਾਉਣ ਵਿੱਚ ਅਸਫਲ ਰਿਹਾ
message.from.whatsapp: |-
{{{text}}}
*📱 ਵਟਸਐਪ ਤੋਂ ਭੇਜਿਆ ਗਿਆ*
📱 *ਵਟਸਐਪ ਤੋਂ ਭੇਜਿਆ ਗਿਆ*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 API ਰਾਹੀਂ ਭੇਜਿਆ ਗਿਆ*
📱 *API ਰਾਹੀਂ ਭੇਜਿਆ ਗਿਆ*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *ਇਹ ਸੁਨੇਹਾ ਵਟਸਐਪ ਵਿੱਚ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਸੀ*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'ਪ੍ਰਸਾਰਣ ਸੂਚੀ'
whatsapp.contact.status.name: 'ਸਥਿਤੀ'
app.connected.message: |-
🔗 **ਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ChatWoot ਇਨਬਾਕਸ**
🔗 **ਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ਇਨਬਾਕਸ**
✅ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਇਸ ChatWoot ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਿਆ ਗਿਆ ਹੈ।
✅ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਇਸ ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਿਆ ਗਿਆ ਹੈ।
ਇਹ ਇਨਬਾਕਸ ਹੁਣ ਤੁਹਾਡੇ **ਵਟਸਐਪ ਖਾਤੇ** ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇੱਥੋਂ ਸਿੱਧੇ ਸੁਨੇਹੇ ਭੇਜ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ — ਬਿਲਕੁਲ ਵਟਸਐਪ ਦੀ ਵਰਤੋਂ ਵਾਂਗ, ਪਰ ChatWoot ਦੇ ਅੰਦਰ।
ਇਹ ਇਨਬਾਕਸ ਹੁਣ ਤੁਹਾਡੇ **ਵਟਸਐਪ ਖਾਤੇ** ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇੱਥੋਂ ਸਿੱਧੇ ਸੁਨੇਹੇ ਭੇਜ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ — ਬਿਲਕੁਲ ਵਟਸਐਪ ਦੀ ਵਰਤੋਂ ਵਾਂਗ, ਪਰ ਇਸ ਵਰਕਸਪੇਸ ਦੇ ਅੰਦਰ।
ਤੁਸੀਂ ਇਸ ਇਨਬਾਕਸ ਨਾਲ ਜੁੜੇ ਵਟਸਐਪ ਸੈਸ਼ਨ ਦਾ ਪ੍ਰਬੰਧਨ ਵੀ ਕਰ ਸਕਦੇ ਹੋ। ਉਪਲਬਧ ਕਾਰਵਾਈਆਂ ਦੇਖਣ ਲਈ, ਇਸ ਚੈਟ ਨੂੰ **help** ਭੇਜੋ।
@@ -104,9 +109,9 @@ app.connected.message: |-
ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ! 🚀
app.disconnected.message: |-
🔌 **ਡਿਸਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ChatWoot ਇਨਬਾਕਸ**
🔌 **ਡਿਸਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ਇਨਬਾਕਸ**
❌ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਇਸ ChatWoot ਇਨਬਾਕਸ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ।
❌ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਇਸ ਇਨਬਾਕਸ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ।
ਤੁਸੀਂ ਹੁਣ ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹੇ ਭੇਜ ਜਾਂ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕੋਗੇ।
@@ -117,33 +122,155 @@ app.updated.message: |-
✅ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਲਈ ਸੈਟਿੰਗਾਂ ਅਪਡੇਟ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ।
app.commands.list: |-
ਤੁਸੀਂ ਹੇਠਾਂ ਦਿੱਤੇ ਕਮਾਂਡਾਂ ਵਿੱਚੋਂ ਇੱਕ ਭੇਜ ਕੇ **ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ** ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹੋ:
cli.usage.arguments: |-
[ਕਾਰਵਾਈ]
🖥️ **ਸੈਸ਼ਨ**:
1. **status** - ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ।
2. **restart** - ਸੈਸ਼ਨ ਮੁੜ ਚਾਲੂ ਕਰੋ - ਇਹ ਤੁਹਾਡੇ ਵਟਸਐਪ ਖਾਤੇ ਨੂੰ ਮੁੜ ਕਨੈਕਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੇਗਾ।
3. **start** - ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ।
4. **stop** - ਸੈਸ਼ਨ ਬੰਦ ਕਰੋ।
5. **logout** - ਸੈਸ਼ਨ ਤੋਂ ਲੌਗਆਉਟ ਕਰੋ, ਤਾਂ ਜੋ ਤੁਸੀਂ QR ਕੋਡ ਸਕੈਨ ਕਰ ਸਕੋ ਅਤੇ ਸੈਸ਼ਨ ਨੂੰ ਮੁਰੰਮਤ ਕਰ ਸਕੋ।
6. **qr** - ਸੈਸ਼ਨ ਲਈ QR ਕੋਡ ਪ੍ਰਾਪਤ ਕਰੋ।
7. **screenshot** - ਸੈਸ਼ਨ ਦਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਪ੍ਰਾਪਤ ਕਰੋ (ਸਿਰਫ **WEBJS** ਇੰਜਣ ਵਿੱਚ ਉਪਲਬਧ)।
8. **help** - ਉਪਲਬਧ ਕਮਾਂਡਾਂ।
cli.usage.command: |-
[ਕਮਾਂਡ]
{{#commands.server}}
⚙️ ਸਰਵਰ:
- **server status** - ਸਰਵਰ ਵਰਜਨ ਅਤੇ ਸਥਿਤੀ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰੋ।
- **server reboot** - ਸਰਵਰ ਨੂੰ ਸੁਚਾਰੂ ਢੰਗ ਨਾਲ ਮੁੜ ਚਾਲੂ ਕਰੋ।
- **server reboot force** - ਸਰਵਰ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਮੁੜ ਚਾਲੂ ਕਰੋ।
{{/commands.server}}
cli.usage.options: |-
[ਚੋਣਾਂ]
ਇਸ ਗੱਲਬਾਤ ਵਿੱਚ **ਕਮਾਂਡ** (ਕੇਸ ਇਨਸੈਂਸਿਟਿਵ) ਜਾਂ **ਕਮਾਂਡ ਨੰਬਰ** ਭੇਜੋ।
app.commands.server.disabled: ⛔ **ਸਰਵਰ ਕਮਾਂਡ ਅਯੋਗ ਹਨ**।
cli.cmd.disabled: ⛔ **`{{command}}` ਕਮਾਂਡ ਨਿਸ਼ਕ੍ਰਿਆ ਕੀਤੀ ਗਈ ਹੈ**।
ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।
ਉਪਲਬਧ ਕਮਾਂਡਾਂ ਦੀ ਸੂਚੀ ਲਈ **help** ਭੇਜੋ।
ਉਪਲਬਧ ਕਮਾਂਡਾਂ ਦੀ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ **help** ਭੇਜੋ।
cli.cmd.root.description: |-
ਆਪਣੀ **ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ** ਦਾ ਪ੍ਰਬੰਧ ਕਰੋ!
cli.cmd.root.sub.session: |-
🖥️ **ਸੈਸ਼ਨ**:
cli.cmd.root.sub.server: |-
🖥️ **ਸਰਵਰ**:
cli.cmd.root.sub.sync: |-
🔄️ **ਸਿੰਕ**:
cli.cmd.session.status.description: |-
ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ।
cli.cmd.session.restart.description: |-
ਸੈਸ਼ਨ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰੋ।
cli.cmd.session.start.description: |-
ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ।
cli.cmd.session.stop.description: |-
ਸੈਸ਼ਨ ਰੋਕੋ।
cli.cmd.session.logout.description: |-
ਨਵਾਂ QR ਕੋਡ ਸਕੈਨ ਕਰਕੇ ਕਨੈਕਸ਼ਨ ਬਹਾਲ ਕਰਨ ਲਈ ਸੈਸ਼ਨ ਤੋਂ ਲੌਗਆਉਟ ਕਰੋ।
cli.cmd.session.qr.description: |-
ਸੈਸ਼ਨ ਦਾ QR ਕੋਡ ਵੇਖਾਓ।
cli.cmd.session.screenshot.description: |-
ਸੈਸ਼ਨ ਦਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲਓ (ਸਿਰਫ਼ **WEBJS** ਇੰਜਨ ਨਾਲ ਉਪਲਬਧ)।
cli.cmd.contacts.summary: |-
ਵਟਸਐਪ ਸੰਪਰਕ ਖਿੱਚੋ।
cli.cmd.contacts.description: |-
👤 ਸਿੰਕ ਸ਼ੁਰੂ ਕਰਨ ਲਈ `contacts pull` ਚਲਾਓ ਜਾਂ ਤਰੱਕੀ ਦੇਖਣ ਲਈ `contacts status` ਚਲਾਓ।
cli.cmd.contacts.action.description: |-
`start` ਨਾਲ ਸੰਪਰਕ ਖਿੱਚਣਾ ਸ਼ੁਰੂ ਕਰੋ ਜਾਂ `status` ਨਾਲ ਸਰਗਰਮ ਕੰਮ ਦੀ ਜਾਂਚ ਕਰੋ।
cli.cmd.contacts.pull.description: |-
ਵਟਸਐਪ ਸੰਪਰਕਾਂ ਨੂੰ ਇਸ ਇਨਬਾਕਸ ਵਿੱਚ ਲਿਆਉਣਾ ਸ਼ੁਰੂ ਕਰੋ।
cli.cmd.contacts.pull.option.batch: |-
ਹਰ ਬੈਚ ਵਿੱਚ ਖਿੱਚੇ ਜਾਣ ਵਾਲੇ ਸੰਪਰਕਾਂ ਦੀ ਗਿਣਤੀ।
cli.cmd.contacts.pull.option.delay-contact: |-
ਹਰ ਸੰਪਰਕ ਦੇ ਵਿਚਕਾਰ ਦੇਰੀ, ਤਾਂ ਜੋ ਅਵਤਾਰ ਖਿੱਚਦੇ ਸਮੇਂ ਰੇਟ ਲਿਮਟ ਤੋਂ ਬਚਿਆ ਜਾ ਸਕੇ।
cli.cmd.contacts.pull.option.delay-batch: |-
ਸੰਪਰਕ ਬੈਚਾਂ ਦੇ ਵਿਚਕਾਰ ਦੇਰੀ।
cli.cmd.contacts.pull.option.avatar: |-
ਚੁਣੋ ਕਿ ਅਵਤਾਰ ਕਿਵੇਂ ਖਿੱਚਣੇ ਹਨ: `skip`, `if-missing`, ਜਾਂ `update`।
cli.cmd.contacts.pull.option.groups: |-
ਸਿੰਕ ਵਿੱਚ WhatsApp ਗਰੁੱਪ ਸੰਪਰਕ ਸ਼ਾਮਲ ਕਰੋ।
cli.cmd.contacts.pull.option.no-attributes: |-
ਜੇ ਸੰਪਰਕ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ ਤਾਂ ਉਸਦੇ ਗੁਣ ਨਾ ਖਿੱਚੋ (ਇਸ ਨਾਲ ਪ੍ਰਕਿਰਿਆ ਤੇਜ਼ ਹੁੰਦੀ ਹੈ)।
cli.cmd.contacts.pull.already-running: |-
⏳ **Pull Contacts** ਕੰਮ ਪਹਿਲਾਂ ਹੀ ਚੱਲ ਰਿਹਾ ਹੈ। ਇਸਦੇ ਮੁਕੰਮਲ ਹੋਣ ਦੀ ਉਡੀਕ ਕਰੋ ਜਾਂ ਨਵਾਂ ਸ਼ੁਰੂ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਇਸਨੂੰ ਰੋਕੋ।
ਵੇਰਵਿਆਂ ਲਈ `contacts status` ਭੇਜੋ।
cli.cmd.options.job.attempts: |-
ਕੰਮ ਨੂੰ ਅਸਫਲ ਘੋਸ਼ਿਤ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ਾਂ ਦੀ ਗਿਣਤੀ।
cli.cmd.options.job.timeout: |-
ਕੰਮ ਨੂੰ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਚੱਲਣ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਸਮਾਂ (ਜਿਵੇਂ `30s`, `5m`, `1h`)।
cli.cmd.contacts.status.description: |-
ਮੌਜੂਦਾ ਖਿੱਚਣ ਵਾਲੇ ਕੰਮ ਦੀ ਸਥਿਤੀ ਵੇਖਾਓ।
cli.cmd.contacts.status.not-found: |-
ℹ️ ਕੋਈ ਸਰਗਰਮ **Pull Contacts** ਕੰਮ ਨਹੀਂ ਮਿਲਿਆ। `contacts pull` ਨਾਲ ਇੱਕ ਸ਼ੁਰੂ ਕਰੋ।
cli.cmd.server.description: |-
ਸਰਵਰ ਦਾ ਪ੍ਰਬੰਧ ਕਰੋ।
cli.cmd.server.status.description: |-
ਸਰਵਰ ਦਾ ਵਰਜਨ ਅਤੇ ਸਥਿਤੀ ਵੇਖਾਓ।
cli.cmd.server.reboot.description: |-
ਸਰਵਰ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰੋ।
cli.cmd.server.reboot.option.force: |-
ਤੁਰੰਤ ਰੀਬੂਟ ਲਾਜ਼ਮੀ ਕਰੋ।
cli.help.usage.title: |-
ਵਰਤੋਂ: ਹੇਠਾਂ ਦਿੱਤੇ ਫਾਰਮੈਟ ਵਿੱਚ ਗੱਲਬਾਤ ਵਿੱਚ ਕਮਾਂਡ ਭੇਜੋ:
cli.help.commands.defaultGroup: |-
**ਕਮਾਂਡਾਂ:**
cli.help.arguments.title: |-
**ਦਲੀਲਾਂ:**
cli.help.options.defaultGroup: |-
**ਚੋਣਾਂ:**
cli.help.globalOptions.title: |-
**ਗਲੋਬਲ ਚੋਣਾਂ:**
task.contacts.status: |-
👤 ਸੰਪਰਕ ਖਿੱਚਣਾ: **{{state}}**
- ✅ ਸਫਲ: {{{progress.ok}}} ਸੰਪਰਕ
- ⚪ ਛੱਡੇ ਗਏ: {{{progress.skipped}}} ਸੰਪਰਕ
- ⚠️ ਗਲਤੀਆਂ: {{{progress.errors}}} ਸੰਪਰਕ
{{#job}}
⌛ ਸਮਾਂ-ਰੇਖਾ:
- ਕਤਾਰ ਵਿੱਚ: {{{timestamp}}}
{{#processedOn}}
- ਸ਼ੁਰੂ: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- ਸਮਾਪਤ: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
ਵੇਰਵੇ:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
ਸੰਪਰਕ ਖਿੱਚੇ ਜਾ ਰਹੇ ਹਨ...
task.contacts.progress: |-
ਸੰਪਰਕ ਖਿੱਚਣਾ: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
ਉਪਲਬਧ ਕਮਾਂਡਾਂ ਦੀ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ **help** ਭੇਜੋ।
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ ਇਹ ChatWoot ਇਨਬਾਕਸ **ਵਟਸਐਪ ਖਾਤੇ** ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਹੈ!
✅ ਇਹ ਇਨਬਾਕਸ ਤੁਹਾਡੇ **ਵਟਸਐਪ ਖਾਤੇ** ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਹੈ!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **ਇਹ ChatWoot ਇਨਬਾਕਸ ਵਟਸਐਪ ਖਾਤੇ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਪਰ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਨਹੀਂ ਹੈ!**
⚠️ **ਇਹ ਇਨਬਾਕਸ ਵਟਸਐਪ ਖਾਤੇ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਪਰ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਨਹੀਂ ਹੈ!**
ਕਿਰਪਾ ਕਰਕੇ ਸੈਸ਼ਨ ਸਥਿਤੀ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜਾਂ ਲੌਗਆਉਟ ਕਰੋ ਅਤੇ QR ਕੋਡ ਦੁਬਾਰਾ ਸਕੈਨ ਕਰੋ।
app.session.scan.qr.code: |-
⚠️ **ਵਟਸਐਪ ਖਾਤੇ ਨੂੰ ਇਸ ChatWoot ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਨ ਲਈ ਆਪਣੇ ਫੋਨ 'ਤੇ QR ਕੋਡ ਸਕੈਨ ਕਰੋ!**
⚠️ **ਵਟਸਐਪ ਖਾਤੇ ਨੂੰ ਇਸ ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਨ ਲਈ ਆਪਣੇ ਫੋਨ 'ਤੇ QR ਕੋਡ ਸਕੈਨ ਕਰੋ!**
app.server.version.and.status: |-
**ਵਰਜਨ:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **ਇਹ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ChatWoot ਵਿੱਚ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।**
⚠️ **ਇਹ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ਇਸ ਇਨਬਾਕਸ ਵਿੱਚ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।**
📱 ਇਸਨੂੰ ਦੇਖਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ **WhatsApp** ਖੋਲ੍ਹੋ।
ਵੇਰਵੇ: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **ਐਲਬਮ ਸੁਨੇਹਾ ਪ੍ਰਾਪਤ ਹੋਇਆ**
{{#totalCount}}
**{{totalCount}}** ਮੀਡੀਆ ਫ਼ਾਈਲ(ਾਂ) ਵਾਲਾ ਇੱਕ ਐਲਬਮ ਪ੍ਰਾਪਤ ਹੋਇਆ।
{{#expectedImageCount}}
📸 ਤਸਵੀਰਾਂ: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 ਵੀਡੀਓ: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
ਮੀਡੀਆ ਫ਼ਾਈਲਾਂ ਅਗਲੇ ਸੁਨੇਹਿਆਂ ਵਿੱਚ ਵਿਤਰਿਤ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ।
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ਫੇਸਬੁੱਕ ਵਿਗਿਆਪਨ**
@@ -352,3 +522,143 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **ਗਾਹਕ ਦਾ ਸੁਨੇਹਾ:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: ਅਣਜਾਣ
ERROR: ਗਲਤੀ
PENDING: ਭੇਜਿਆ ਜਾ ਰਿਹਾ ਹੈ
SERVER: ਸਰਵਰ
DEVICE: ਜੰਤਰ
READ: ਪੜ੍ਹਿਆ ਗਿਆ
PLAYED: ਚਲਾਇਆ ਗਿਆ
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **ਸੁਨੇਹੇ ਵਿੱਚ ਮੀਡੀਆ ਹੈ ਪਰ ਅਸੀਂ ਇਸਨੂੰ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕਰ ਸਕੇ**
📱 ਕਿਰਪਾ ਕਰਕੇ ਵੇਖਣ ਲਈ **WhatsApp** ਖੋਲ੍ਹੋ।
{{^content}}
ਵੇਰਵੇ: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **ਕਤਾਰ**:'
cli.cmd.messages.summary: WhatsApp ਸੁਨੇਹੇ ਖਿੱਚੋ।
cli.cmd.messages.description:
💬 ਸੁਨੇਹੇ ਸਿੰਕ ਕਰਨ ਲਈ `messages pull` ਚਲਾਓ ਜਾਂ ਪ੍ਰਗਤੀ ਦੇਖਣ ਲਈ `messages
status` ਚਲਾਓ।
cli.cmd.messages.action.description:
ਸੁਨੇਹੇ ਖਿੱਚਣ ਲਈ `pull` ਜਾਂ ਸਕ੍ਰਿਆ ਕੰਮ ਦੇਖਣ ਲਈ `status` ਵਰਤੋਂ।
cli.cmd.messages.pull.option.chat:
ਖਿੱਚਣ ਨੂੰ ਕਿਸੇ ਖਾਸ WhatsApp ਚੈਟ ID ਤੱਕ ਸੀਮਿਤ ਕਰੋ।
cli.cmd.messages.pull.option.force:
ਜੇ WhatsApp ਸੁਨੇਹਾ ਪਹਿਲਾਂ ਹੀ ਗੱਲਬਾਤ ਦੇ ਮੈਪ ਵਿੱਚ ਹੈ ਤਾਂ ਵੀ ਇਨਬਾਕਸ ਨੂੰ ਸੁਨੇਹੇ
ਬਣਾਉਣ ਲਈ ਮਜਬੂਰ ਕਰੋ।
cli.cmd.messages.pull.option.batch:
ਹਰ ਬੈਚ ਵਿੱਚ ਖਿੱਚੇ ਜਾਣ ਵਾਲੇ ਸੁਨੇਹਿਆਂ ਦੀ ਗਿਣਤੀ।
cli.cmd.messages.pull.option.progress:
ਹਰ N ਸੁਨੇਹਿਆਂ ’ਤੇ ਪ੍ਰਗਤੀ ਦੀ ਰਿਪੋਰਟ ਕਰੋ। ਰਿਪੋਰਟਾਂ ਬੰਦ ਕਰਨ ਲਈ 0 ਸੈੱਟ ਕਰੋ।
cli.cmd.messages.pull.option.no-dm: ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ ਡਾਇਰੈਕਟ ਸੁਨੇਹੇ ਛੱਡੋ।
cli.cmd.messages.pull.option.groups:
ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ WhatsApp ਗਰੁੱਪ ਸ਼ਾਮਲ ਕਰੋ।
cli.cmd.messages.pull.option.channels:
ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ WhatsApp ਚੈਨਲ ਸ਼ਾਮਲ ਕਰੋ।
cli.cmd.messages.pull.option.status:
ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ WhatsApp ਸਟੇਟਸ ਅੱਪਡੇਟ ਸ਼ਾਮਲ ਕਰੋ।
cli.cmd.messages.pull.option.broadcast:
ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ ਪ੍ਰਸਾਰਣ ਸੂਚੀਆਂ ਸ਼ਾਮਲ ਕਰੋ।
cli.cmd.messages.pull.option.media:
ਇਤਿਹਾਸ ਖਿੱਚਦੇ ਸਮੇਂ ਮੀਡੀਆ (ਤਸਵੀਰਾਂ, ਵੀਡੀਓ, ਫਾਈਲਾਂ) ਡਾਊਨਲੋਡ ਕਰੋ।
cli.cmd.messages.pull.option.pause:
ਸੁਨੇਹੇ ਖਿੱਚਣ ਦਾ ਕੰਮ ਠਹਿਰੇ ਹੋਏ ਹਾਲਤ ਵਿੱਚ ਕਤਾਰ ਵਿੱਚ ਪਾਓ ਅਤੇ ਮੁਕੰਮਲ ਹੋਣ ਤੱਕ ਨਵੇਂ
WhatsApp ਸੁਨੇਹਿਆਂ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੋਕੋ।
cli.cmd.messages.pull.option.resolve-conversations:
ਸਾਰੇ ਇਤਿਹਾਸਿਕ ਸੁਨੇਹੇ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਤੋਂ ਬਾਅਦ ਗੱਲਬਾਤਾਂ ਨੂੰ ਹੱਲ ਹੋਇਆ ਚਿੰਨ੍ਹਤ ਕਰਦਾ
ਹੈ।
cli.cmd.messages.pull.error.pause-no-queue:
⛔ `--pause` ਵਰਤ ਨਹੀਂ ਸਕਦੇ ਕਿਉਂਕਿ ਇਸ ਸੰਰਚਨਾ ਵਿੱਚ **queue** ਹੁਕਮ ਅਸਮਰੱਥ ਹੈ।
cli.cmd.queue.description: ਕਤਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ।
cli.cmd.queue.summary: ਕਤਾਰਾਂ ’ਤੇ ਕਾਰਵਾਈਆਂ ਕਰੋ।
cli.cmd.queue.action.description: ਇੱਕ ਕਾਰਵਾਈ ਚੁਣੋ।
cli.cmd.queue.status.description: ਕਤਾਰ ਦੀ ਸਥਿਤੀ ਦਿਖਾਓ।
cli.cmd.queue.argument.name: 'ਕਤਾਰ ਦਾ ਨਾਮ: `inbox` ਜਾਂ `whatsapp` (ਵਿਕਲਪਿਕ)।'
cli.cmd.queue.start.description: ਨਾਂ ਦੁਆਰਾ ਕਤਾਰ ਸ਼ੁਰੂ ਕਰੋ।
cli.cmd.queue.stop.description: ਨਾਂ ਦੁਆਰਾ ਕਤਾਰ ਰੋਕੋ।
cli.cmd.queue.paused:
⚪🧩 ਕਤਾਰਾਂ ਨੂੰ ਰੋਕ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਸਥਿਤੀ ਜਾਂਚਣ ਲਈ `queue status` ਭੇਜੋ।
cli.cmd.queue.resumed:
🟢🧩 ਕਤਾਰਾਂ ਮੁੜ ਚਾਲੂ ਹੋ ਚੁੱਕੀਆਂ ਹਨ। ਸਥਿਤੀ ਜਾਂਚਣ ਲਈ `queue status` ਭੇਜੋ।
cli.cmd.queue.status.result: >-
📊 ਕਤਾਰ ਦੀ ਸਥਿਤੀ:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ ਰੋਕੀ
ਗਈ{{/paused}}{{^paused}}🟢 ਚੱਲ ਰਹੀ{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
ਹਰ ਸੁਨੇਹੇ ਲਈ ਮੀਡੀਆ ਡਾਊਨਲੋਡ ਕਰਨ ਦਾ ਸਮਾਂ ਨਿਰਧਾਰਤ ਕਰੋ।
cli.cmd.messages.pull.argument.end:
ਖਿੱਚਣ ਦੀ ਵਿੰਡੋ ਕਿੱਥੇ ਖਤਮ ਹੁੰਦੀ ਹੈ ਨਿਰਧਾਰਤ ਕਰੋ; ਜਿਵੇਂ `2d` ਦੋ ਦਿਨ ਪਹਿਲਾਂ ਰੁਕਦਾ
ਹੈ।
cli.cmd.messages.pull.argument.start:
ਖਿੱਚਣ ਦੀ ਵਿੰਡੋ ਕਿੱਥੋਂ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ ਨਿਰਧਾਰਤ ਕਰੋ; ਜਿਵੇਂ `1d` ਇੱਕ ਦਿਨ ਪਹਿਲਾਂ
ਤੋਂ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ (ਡਿਫ਼ਾਲਟ `0d`, ਅਰਥਾਤ ਹੁਣ)।
cli.cmd.messages.pull.already-running: >-
⏳ **Pull Messages** ਕੰਮ ਪਹਿਲਾਂ ਹੀ **{{{chat}}}** ਲਈ ਚੱਲ ਰਿਹਾ ਹੈ। ਨਵਾਂ ਸ਼ੁਰੂ
ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਇਸਦੇ ਮੁਕੰਮਲ ਹੋਣ ਦੀ ਉਡੀਕ ਕਰੋ ਜਾਂ ਇਸਨੂੰ ਰੋਕੋ।
ਵੇਰਵੇ ਲਈ `messages status` ਭੇਜੋ।
cli.cmd.messages.status.not-found:
ℹ️ ਕੋਈ ਸਰਗਰਮ **Pull Messages** ਕੰਮ ਨਹੀਂ ਮਿਲਿਆ। `messages pull` ਨਾਲ ਇੱਕ ਸ਼ੁਰੂ
ਕਰੋ।
cli.cmd.messages.pull.no-chats-found: ਇਸ ਅਵਧੀ ਲਈ ਕੋਈ WhatsApp ਚੈਟ ਨਹੀਂ ਮਿਲੀ।
cli.cmd.contacts.pull.option.progress:
ਹਰ N ਸੰਪਰਕ ’ਤੇ ਪ੍ਰਗਤੀ ਦੱਸੋ। ਰਿਪੋਰਟਾਂ ਬੰਦ ਕਰਨ ਲਈ 0 ਸੈੱਟ ਕਰੋ।
cli.cmd.contacts.pull.option.lids:
ਸਿੰਕ ਵਿੱਚ WhatsApp LID (ਅਣਪਛਾਤੇ) ਸੰਪਰਕ ਸ਼ਾਮਲ ਕਰੋ।
task.contacts.completed:
👤✅ ਸੰਪਰਕ ਮੁਕੰਮਲ → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started:
⏳ ਸੁਨੇਹੇ ਖਿੱਚੇ ਜਾ ਰਹੇ ਹਨ 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details: ਵਧੇਰੇ ਵੇਰਵਿਆਂ ਲਈ "{{{prefix}}}messages status" ਭੇਜੋ।
task.messages.progress:
'⏳ ਸੁਨੇਹੇ {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ ਸੁਨੇਹੇ {{{chat}}} ਵਿਚ {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
**{{{chat}}}** ਦੇ ਸੁਨੇਹੇ ⏱️ **{{{period}}}** ਸਮੇਂ ਵਿੱਚ। ⚙️ ਕੰਮ ਦੀ ਸਥਿਤੀ: **{{state}}**।
🗨️ ਸੁਨੇਹੇ:
- 🆕 ਬਣਾਇਆ: **{{{progress.ok}}}**
- 🟢 ਪਹਿਲਾਂ ਤੋਂ ਮੌਜੂਦ: {{{progress.exists}}}
- ⚪ ਅਣਦੇਖੇ: {{{progress.ignored}}}
- 🔴 ਗਲਤੀਆਂ: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ ਸਭ ਤੋਂ ਪੁਰਾਣਾ: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ ਸਭ ਤੋਂ ਨਵਾਂ: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp ਚੈਟ:
- 🆕 **{{{chats}}}** ਚੈਟਾਂ ਵਿੱਚ ਨਵੇਂ ਸੁਨੇਹੇ
{{#options.resolveConversations}}
🗣️ ਗੱਲਬਾਤਾਂ:
- 🟢 ਹੱਲ ਹੋਈਆਂ: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 ਗਲਤੀਆਂ: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
ਵੇਰਵੇ:
[{{{details.text}}}]({{{details.url}}})
+355 -40
View File
@@ -1,4 +1,4 @@
# Portuguese translations for ChatWoot integration
# Portuguese translations for WhatsApp inbox integration
locale.name: '🇧🇷🇵🇹 Português (Portuguese)'
# Código de locale base - deve ser um locale BCP 47 válido
@@ -6,22 +6,25 @@ locale.name: '🇧🇷🇵🇹 Português (Portuguese)'
# A WAHA usa isso para formatar números, moeda e data/hora
locale.base: 'pt-BR'
app.inbox.contact.name: 'Integração WhatsApp (WAHA)'
app.inbox.contact.name: 'Integração WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opções" de date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: pt-BR
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
timeZone: 'America/Sao_Paulo'
timeZoneName: short
Today: Hoje
Yesterday: Ontem
job.report.error: |-
⚠️ **{{{header}}}**
@@ -54,14 +57,14 @@ whatsapp.message.removing.error:
job.scheduled.error.header: Falha ao executar tarefa agendada
message.from.whatsapp: |-
{{{text}}}
*📱 Enviado do WhatsApp*
📱 *Enviado do WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Enviado via API*
📱 *Enviado via API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Esta mensagem foi excluída no WhatsApp*
@@ -89,11 +92,11 @@ whatsapp.contact.broadcast.suffix: 'Lista de transmissão'
whatsapp.contact.status.name: 'Status'
app.connected.message: |-
🔗 **CONECTADO: Sessão do WhatsApp para Caixa de Entrada do ChatWoot**
🔗 **CONECTADO: Sessão do WhatsApp para a caixa de entrada**
✅ A sessão do WhatsApp **{{{name}}}** foi conectada com sucesso a esta Caixa de Entrada do ChatWoot.
✅ A sessão do WhatsApp **{{{name}}}** foi conectada com sucesso a esta caixa de entrada.
Esta caixa de entrada agora está vinculada à sua **Conta do WhatsApp**. Você pode enviar e receber mensagens diretamente daqui — assim como usar o WhatsApp, mas dentro do ChatWoot.
Esta caixa de entrada agora está vinculada à sua **Conta do WhatsApp**. Você pode enviar e receber mensagens diretamente daqui — assim como usar o WhatsApp, mas dentro do seu espaço de trabalho.
Você também pode gerenciar a sessão do WhatsApp vinculada a esta caixa de entrada. Para ver as ações disponíveis, envie **help** para este chat.
@@ -107,9 +110,9 @@ app.connected.message: |-
Vamos começar! 🚀
app.disconnected.message: |-
🔌 **DESCONECTADO: Sessão do WhatsApp da Caixa de Entrada do ChatWoot**
🔌 **DESCONECTADO: Sessão do WhatsApp da caixa de entrada**
❌ A sessão do WhatsApp **{{{name}}}** foi desconectada desta Caixa de Entrada do ChatWoot.
❌ A sessão do WhatsApp **{{{name}}}** foi desconectada desta caixa de entrada.
Você não poderá mais enviar ou receber mensagens do WhatsApp.
@@ -120,34 +123,156 @@ app.updated.message: |-
✅ As configurações para a sessão do WhatsApp **{{{name}}}** foram atualizadas.
app.commands.list: |-
Você pode gerenciar a **Integração WhatsApp** enviando um dos seguintes comandos:
cli.usage.arguments: |-
[ação]
🖥️ **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.
4. **stop** - parar a sessão.
5. **logout** - sair da sessão, para que você possa escanear o código QR e reparar a sessão.
6. **qr** - obter o código QR para a sessão.
7. **screenshot** - obter uma captura de tela da sessão (disponível apenas no motor **WEBJS**).
8. **help** - comandos disponíveis.
cli.usage.command: |-
[comando]
{{#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}}
cli.usage.options: |-
[opções]
Envie um **comando** (não sensível a maiúsculas/minúsculas) ou um **número de comando** para esta conversa.
cli.cmd.disabled: ⛔ **O comando `{{command}}` está desativado**.
app.commands.server.disabled: ⛔ **Os comandos do servidor estão desativados**.
Por favor, contate o seu administrador.
Entre em contato com seu administrador.
Envie **help** para obter a lista de comandos disponíveis.
cli.cmd.root.description: |-
Gerencie sua **Integração com WhatsApp**!
cli.cmd.root.sub.session: |-
🖥️ **Sessão**:
cli.cmd.root.sub.server: |-
🖥️ **Servidor**:
cli.cmd.root.sub.sync: |-
🔄️ **Sincronização**:
cli.cmd.session.status.description: |-
Obtenha o status atual da sessão.
cli.cmd.session.restart.description: |-
Reinicie a sessão.
cli.cmd.session.start.description: |-
Inicie a sessão.
cli.cmd.session.stop.description: |-
Pare a sessão.
cli.cmd.session.logout.description: |-
Saia da sessão para escanear um novo código QR e restaurar a conexão.
cli.cmd.session.qr.description: |-
Mostre o código QR da sessão.
cli.cmd.session.screenshot.description: |-
Faça uma captura de tela da sessão (disponível apenas com o mecanismo **WEBJS**).
cli.cmd.contacts.summary: |-
Buscar contatos do WhatsApp.
cli.cmd.contacts.description: |-
👤 Execute `contacts pull` para iniciar uma sincronização ou `contacts status` para acompanhar o progresso.
cli.cmd.contacts.action.description: |-
Use `start` para iniciar a busca de contatos ou `status` para conferir o trabalho ativo.
cli.cmd.contacts.pull.description: |-
Comece a importar contatos do WhatsApp para esta caixa de entrada.
cli.cmd.contacts.pull.option.batch: |-
Quantidade de contatos buscados em cada lote.
cli.cmd.contacts.pull.option.delay-contact: |-
Atraso entre cada contato para evitar limites ao buscar avatares.
cli.cmd.contacts.pull.option.delay-batch: |-
Atraso entre os lotes de contatos.
cli.cmd.contacts.pull.option.avatar: |-
Escolha como buscar os avatares dos contatos: `skip`, `if-missing` ou `update`.
cli.cmd.contacts.pull.option.groups: |-
Incluir contatos de grupo do WhatsApp na sincronização.
cli.cmd.contacts.pull.option.no-attributes: |-
Não busque atributos do contato se ele já existir (acelera o processo).
cli.cmd.contacts.pull.already-running: |-
⏳ O trabalho **Pull Contacts** já está em execução. Aguarde terminar ou interrompa-o antes de iniciar um novo.
Envie `contacts status` para obter detalhes.
cli.cmd.options.job.attempts: |-
Número de tentativas antes de marcar o trabalho como falho.
cli.cmd.options.job.timeout: |-
Tempo máximo permitido antes de tentar novamente (aceita valores como `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Mostrar o status do trabalho de busca atual.
cli.cmd.contacts.status.not-found: |-
ℹ️ Nenhum trabalho ativo **Pull Contacts** encontrado. Inicie um com `contacts pull`.
cli.cmd.server.description: |-
Gerencie o servidor.
cli.cmd.server.status.description: |-
Mostre a versão e o status do servidor.
cli.cmd.server.reboot.description: |-
Reinicie o servidor.
cli.cmd.server.reboot.option.force: |-
Forçar uma reinicialização imediata.
cli.help.usage.title: |-
Uso: envie um comando para a conversa no formato:
cli.help.commands.defaultGroup: |-
**Comandos:**
cli.help.arguments.title: |-
**Argumentos:**
cli.help.options.defaultGroup: |-
**Opções:**
cli.help.globalOptions.title: |-
**Opções globais:**
task.contacts.status: |-
👤 Busca de contatos: **{{state}}**
- ✅ Sucesso: {{{progress.ok}}} contatos
- ⚪ Ignorados: {{{progress.skipped}}} contatos
- ⚠️ Erros: {{{progress.errors}}} contatos
{{#job}}
⌛ Linha do tempo:
- Na fila: {{{timestamp}}}
{{#processedOn}}
- Início: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Fim: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Detalhes:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Buscando contatos...
task.contacts.progress: |-
Busca de contatos: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Envie **help** para obter a lista de comandos disponíveis.
@@ -161,17 +286,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Esta Caixa de Entrada do ChatWoot está vinculada à **Conta do WhatsApp** e pronta para enviar e receber mensagens!
✅ Esta caixa de entrada está vinculada à sua **Conta do WhatsApp** e pronta para enviar e receber mensagens!
📱 **{{{name}}} ({{{id}}})**
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!**
⚠️ **Esta caixa de entrada 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: |-
⚠️ **Escaneie o código QR no seu telefone para vincular a Conta do WhatsApp a esta Caixa de Entrada do ChatWoot!**
⚠️ **Escaneie o código QR no seu telefone para vincular a Conta do WhatsApp a esta caixa de entrada!**
app.server.version.and.status: |-
**Versão:**
@@ -286,6 +411,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -340,11 +493,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Esse tipo de mensagem não é suportado no ChatWoot.**
⚠️ **Esse tipo de mensagem não é suportado nesta caixa de entrada.**
📱 Por favor, abra o **WhatsApp** para visualizá-la.
Detalhes: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Mensagem de Álbum Recebida**
{{#totalCount}}
Recebido um álbum com **{{totalCount}}** arquivo(s) de mídia.
{{#expectedImageCount}}
📸 Imagens: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Vídeos: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Os arquivos de mídia serão entregues nas mensagens seguintes.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ANÚNCIO DO FACEBOOK**
@@ -359,3 +527,150 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **MENSAGEM DO CLIENTE:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Desconhecido
ERROR: Erro
PENDING: Enviando
SERVER: Servidor
DEVICE: Dispositivo
READ: Lido
PLAYED: Reproduzido
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **A mensagem tem mídia, mas não conseguimos fazer o download**
📱 Abra o **WhatsApp** para visualizá-la.
{{^content}}
Detalhes: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Fila**:'
cli.cmd.messages.summary: Buscar mensagens do WhatsApp.
cli.cmd.messages.description:
💬 Execute `messages pull` para solicitar a sincronização de mensagens ou
`messages status` para acompanhar o progresso.
cli.cmd.messages.action.description:
'`pull` para iniciar a busca de mensagens ou `status` para inspecionar o
trabalho em andamento.'
cli.cmd.messages.pull.option.chat:
Limita a busca a um ID de conversa do WhatsApp específico.
cli.cmd.messages.pull.option.force:
Força a caixa de entrada a criar mensagens mesmo quando a mensagem do WhatsApp
já existe no mapeamento da conversa.
cli.cmd.messages.pull.option.batch:
Quantidade de mensagens buscadas em cada lote.
cli.cmd.messages.pull.option.progress:
Relata o progresso a cada N mensagens. Defina 0 para desativar os relatórios
de progresso.
cli.cmd.messages.pull.option.no-dm:
Ignora mensagens diretas ao buscar o histórico.
cli.cmd.messages.pull.option.groups:
Inclui conversas em grupo do WhatsApp ao buscar o histórico.
cli.cmd.messages.pull.option.channels:
Inclui canais do WhatsApp ao buscar o histórico.
cli.cmd.messages.pull.option.status:
Inclui atualizações de status do WhatsApp ao buscar o histórico.
cli.cmd.messages.pull.option.broadcast:
Inclui listas de transmissão ao buscar o histórico.
cli.cmd.messages.pull.option.media:
Baixa anexos de mídia (imagens, vídeos, arquivos) ao buscar o histórico.
cli.cmd.messages.pull.option.pause:
Enfileira o trabalho de busca de mensagens em estado pausado, interrompendo o
processamento de novos WhatsApp até finalizar.
cli.cmd.messages.pull.option.resolve-conversations:
Marca as conversas como resolvidas depois que todas as mensagens do histórico
forem processadas.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ Não é possível usar `--pause` porque o comando **queue** está desabilitado
nesta configuração.
cli.cmd.queue.description: Gerencie filas.
cli.cmd.queue.summary: Execute ações nas filas.
cli.cmd.queue.action.description: Escolha uma ação.
cli.cmd.queue.status.description: Mostra o status de uma fila.
cli.cmd.queue.argument.name: 'Nome da fila: `inbox` ou `whatsapp` (opcional).'
cli.cmd.queue.start.description: Inicia uma fila pelo nome.
cli.cmd.queue.stop.description: Para uma fila pelo nome.
cli.cmd.queue.paused:
⚪🧩 As filas foram pausadas. Envie `queue status` para verificar o status.
cli.cmd.queue.resumed:
🟢🧩 As filas foram retomadas. Envie `queue status` para verificar o status.
cli.cmd.queue.status.result: >-
📊 Status da fila:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ Em
pausa{{/paused}}{{^paused}}🟢 Em execução{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Define por quanto tempo tentar baixar a mídia de cada mensagem.
cli.cmd.messages.pull.argument.end:
Define até onde a janela de busca termina; por exemplo, `2d` encerra há dois
dias.
cli.cmd.messages.pull.argument.start:
Define onde a janela de busca começa; por exemplo, `1d` inicia há um dia
(padrão `0d`, ou seja, agora).
cli.cmd.messages.pull.already-running: >-
⏳ O trabalho **Pull Messages** já está em execução para **{{{chat}}}**.
Aguarde terminar ou pare antes de iniciar outro.
Envie `messages status` para mais detalhes.
cli.cmd.messages.status.not-found:
ℹ️ Nenhum trabalho ativo de **Pull Messages** foi encontrado. Inicie um com
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
Nenhuma conversa do WhatsApp encontrada para buscar mensagens nesse período.
cli.cmd.contacts.pull.option.progress:
Informa o progresso a cada N contatos. Defina 0 para desativar os relatórios
de progresso.
cli.cmd.contacts.pull.option.lids:
Inclui contatos LID do WhatsApp (contatos anônimos) na sincronização.
task.contacts.completed:
👤✅ Contatos concluídos → 🆕 {{progress.created}}, 🔄 {{progress.updated}},
⚪ {{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️
{{progress.avatar.updated}}
task.messages.started: ⏳ Buscando mensagens 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Envie "{{{prefix}}}messages status" para ver mais detalhes.
task.messages.progress:
'⏳ Mensagens {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Mensagens {{{chat}}} em {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Mensagens **{{{chat}}}** no período ⏱️ **{{{period}}}**. ⚙️ Estado do trabalho: **{{state}}**.
🗨️ Mensagens:
- 🆕 Criadas: **{{{progress.ok}}}**
- 🟢 Já existem: {{{progress.exists}}}
- ⚪ Ignoradas: {{{progress.ignored}}}
- 🔴 Erros: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Mais antiga: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Mais recente: **{{{messages.last}}}**
{{/messages.last}}
💬 Conversas do WhatsApp:
- 🆕 Novas mensagens em **{{{chats}}}** conversas
{{#options.resolveConversations}}
🗣️ Conversas:
- 🟢 Resolvidas: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Erros: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Detalhes:
[{{{details.text}}}]({{{details.url}}})
+351 -38
View File
@@ -1,4 +1,4 @@
# Russian translations for ChatWoot integration
# Russian translations for WhatsApp inbox integration
locale.name: '🇷🇺 Русский (Russian)'
# Базовый код локали - должен быть корректной локалью BCP 47
@@ -6,23 +6,28 @@ locale.name: '🇷🇺 Русский (Russian)'
# WAHA использует его для форматирования чисел, валют и даты/времени
locale.base: 'ru-RU'
app.inbox.contact.name: 'WhatsApp Интеграция (WAHA)'
app.inbox.contact.name: 'WhatsApp Интеграция'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "параметры" из date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: ru-RU
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Moscow'
Today: 'Сегодня'
Yesterday: 'Вчера'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: Не удалось удалить сообще
job.scheduled.error.header: Запланированная задача не выполнена
message.from.whatsapp: |-
{{{text}}}
*📱 Отправлено из WhatsApp*
📱 *Отправлено из WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Отправлено через API*
📱 *Отправлено через API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Это сообщение было удалено в WhatsApp*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Список рассылки'
whatsapp.contact.status.name: 'Статус'
app.connected.message: |-
🔗 **ПОДКЛЮЧЕНО: Сессия WhatsApp к ChatWoot Inbox**
🔗 **ПОДКЛЮЧЕНО: Сессия WhatsApp к Inbox**
✅ Сессия WhatsApp **{{{name}}}** была успешно подключена к этому ChatWoot Inbox.
✅ Сессия WhatsApp **{{{name}}}** была успешно подключена к этому Inbox.
Этот inbox теперь связан с вашим **аккаунтом WhatsApp**. Вы можете отправлять и получать сообщения прямо отсюда — как в WhatsApp, но внутри ChatWoot.
Этот inbox теперь связан с вашим **аккаунтом WhatsApp**. Вы можете отправлять и получать сообщения прямо отсюда — как в WhatsApp, но внутри вашего рабочего пространства.
Вы также можете управлять сессией WhatsApp, связанной с этим inbox. Чтобы увидеть доступные действия, отправьте **help** в этот чат.
@@ -104,9 +109,9 @@ app.connected.message: |-
Давайте начнем! 🚀
app.disconnected.message: |-
🔌 **ОТКЛЮЧЕНО: Сессия WhatsApp от ChatWoot Inbox**
🔌 **ОТКЛЮЧЕНО: Сессия WhatsApp от Inbox**
❌ Сессия WhatsApp **{{{name}}}** была отключена от этого ChatWoot Inbox.
❌ Сессия WhatsApp **{{{name}}}** была отключена от этого Inbox.
Вы больше не сможете отправлять или получать сообщения из WhatsApp.
@@ -117,34 +122,156 @@ app.updated.message: |-
✅ Настройки для сессии WhatsApp **{{{name}}}** были обновлены.
app.commands.list: |-
Вы можете управлять **WhatsApp Интеграцией**, отправив одну из следующих команд:
cli.usage.arguments: |-
[действие]
🖥️ **Сессия**:
1. **status** - получить текущий статус сессии.
2. **restart** - перезапустить сессию - это попытается переподключить ваш аккаунт WhatsApp.
3. **start** - запустить сессию.
4. **stop** - остановить сессию.
5. **logout** - выйти из сессии, чтобы вы могли отсканировать QR-код и восстановить сессию.
6. **qr** - получить QR-код для сессии.
7. **screenshot** - получить скриншот сессии (доступно только в движке **WEBJS**).
8. **help** - доступные команды.
cli.usage.command: |-
[команда]
{{#commands.server}}
⚙️ Сервер:
- **server status** - получить информацию о версии и статусе сервера.
- **server reboot** - корректно перезагрузить сервер.
- **server reboot force** - принудительно перезагрузить сервер.
{{/commands.server}}
cli.usage.options: |-
[опции]
Отправьте **команду** (без учета регистра) или **номер команды** в этот разговор.
cli.cmd.disabled: ⛔ **Команда `{{command}}` отключена**.
app.commands.server.disabled: ⛔ **Команды сервера отключены**.
Пожалуйста, свяжитесь с вашим администратором.
Пожалуйста, обратитесь к вашему администратору.
Отправьте **help**, чтобы получить список доступных команд.
cli.cmd.root.description: |-
Управляйте своей **интеграцией WhatsApp**!
cli.cmd.root.sub.session: |-
🖥️ **Сессия**:
cli.cmd.root.sub.server: |-
🖥️ **Сервер**:
cli.cmd.root.sub.sync: |-
🔄️ **Синхронизация**:
cli.cmd.session.status.description: |-
Получите текущий статус сессии.
cli.cmd.session.restart.description: |-
Перезапустите сессию.
cli.cmd.session.start.description: |-
Запустите сессию.
cli.cmd.session.stop.description: |-
Остановите сессию.
cli.cmd.session.logout.description: |-
Выйдите из сессии, чтобы отсканировать новый QR-код и восстановить соединение.
cli.cmd.session.qr.description: |-
Покажите QR-код сессии.
cli.cmd.session.screenshot.description: |-
Сделайте снимок экрана сессии (доступно только с движком **WEBJS**).
cli.cmd.contacts.summary: |-
Загрузить контакты WhatsApp.
cli.cmd.contacts.description: |-
👤 Выполните `contacts pull`, чтобы запустить синхронизацию, или `contacts status`, чтобы проверить прогресс.
cli.cmd.contacts.action.description: |-
Используйте `start`, чтобы начать загрузку контактов, или `status`, чтобы проверить активное задание.
cli.cmd.contacts.pull.description: |-
Начните импорт контактов WhatsApp в inbox.
cli.cmd.contacts.pull.option.batch: |-
Количество контактов в каждой партии.
cli.cmd.contacts.pull.option.delay-contact: |-
Задержка между обработкой контактов, чтобы избежать ограничений при загрузке аватаров.
cli.cmd.contacts.pull.option.delay-batch: |-
Задержка между партиями контактов.
cli.cmd.contacts.pull.option.avatar: |-
Выберите, как загружать аватары контактов: `skip`, `if-missing` или `update`.
cli.cmd.contacts.pull.option.groups: |-
Включить контакты групп WhatsApp в синхронизацию.
cli.cmd.contacts.pull.option.no-attributes: |-
Не загружать атрибуты контакта, если он уже существует (ускоряет процесс).
cli.cmd.contacts.pull.already-running: |-
⏳ Задание **Pull Contacts** уже выполняется. Подождите его завершения или остановите перед запуском нового.
Отправьте `contacts status` для подробностей.
cli.cmd.options.job.attempts: |-
Число повторных попыток до пометки задания как неуспешного.
cli.cmd.options.job.timeout: |-
Максимальное время выполнения перед повторной попыткой (поддерживает значения `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Показать статус текущего задания загрузки.
cli.cmd.contacts.status.not-found: |-
ℹ️ Активное задание **Pull Contacts** не найдено. Запустите его командой `contacts pull`.
cli.cmd.server.description: |-
Управляйте сервером.
cli.cmd.server.status.description: |-
Покажите версию и статус сервера.
cli.cmd.server.reboot.description: |-
Перезагрузите сервер.
cli.cmd.server.reboot.option.force: |-
Принудительно выполните немедленную перезагрузку.
cli.help.usage.title: |-
Использование: отправьте команду в диалог в следующем формате:
cli.help.commands.defaultGroup: |-
**Команды:**
cli.help.arguments.title: |-
**Аргументы:**
cli.help.options.defaultGroup: |-
**Опции:**
cli.help.globalOptions.title: |-
**Глобальные опции:**
task.contacts.status: |-
👤 Загрузка контактов: **{{state}}**
- ✅ Успешно: {{{progress.ok}}} контактов
- ⚪ Пропущено: {{{progress.skipped}}} контактов
- ⚠️ Ошибки: {{{progress.errors}}} контактов
{{#job}}
⌛ Хронология:
- В очереди: {{{timestamp}}}
{{#processedOn}}
- Начато: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Завершено: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Подробности:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Идёт загрузка контактов...
task.contacts.progress: |-
Загрузка контактов: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Отправьте **help**, чтобы получить список доступных команд.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Этот ChatWoot Inbox связан с **аккаунтом WhatsApp** и готов отправлять и получать сообщения!
✅ Этот inbox связан с вашим **аккаунтом WhatsApp** и готов отправлять и получать сообщения!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **Этот ChatWoot Inbox связан с аккаунтом WhatsApp, но НЕ готов отправлять и получать сообщения!**
⚠️ **Этот inbox связан с аккаунтом WhatsApp, но НЕ готов отправлять и получать сообщения!**
Пожалуйста, проверьте статус сессии и попробуйте перезапустить её или выйдите и отсканируйте QR-код снова.
app.session.scan.qr.code: |-
⚠️ **Отсканируйте QR-код на вашем телефоне, чтобы связать аккаунт WhatsApp с этим ChatWoot Inbox!**
⚠️ **Отсканируйте QR-код на вашем телефоне, чтобы связать аккаунт WhatsApp с этим Inbox!**
app.server.version.and.status: |-
**Версия:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Этот тип сообщения не поддерживается в ChatWoot.**
⚠️ **Этот тип сообщения не поддерживается в этом Inbox.**
📱 Пожалуйста, откройте **WhatsApp**, чтобы просмотреть.
Подробности: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Получено сообщение с альбомом**
{{#totalCount}}
Получен альбом с **{{totalCount}}** медиафайлом(ами).
{{#expectedImageCount}}
📸 Изображений: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Видео: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Медиафайлы будут доставлены в следующих сообщениях.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **РЕКЛАМА FACEBOOK**
@@ -352,3 +522,146 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Сообщение клиента:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Неизвестно
ERROR: Ошибка
PENDING: Отправляется
SERVER: Сервер
DEVICE: Устройство
READ: Прочитано
PLAYED: Прослушано
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **В сообщении есть медиа, но нам не удалось его скачать**
📱 Откройте **WhatsApp**, чтобы посмотреть.
{{^content}}
Подробности: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Очередь**:'
cli.cmd.messages.summary: Загрузить сообщения WhatsApp.
cli.cmd.messages.description:
💬 Выполните `messages pull`, чтобы запустить синхронизацию, или `messages
status`, чтобы проверить прогресс.
cli.cmd.messages.action.description:
'`pull` — начать загрузку, `status` — посмотреть текущую задачу.'
cli.cmd.messages.pull.option.chat:
Ограничить загрузку конкретным идентификатором чата WhatsApp.
cli.cmd.messages.pull.option.force:
Принудительно создавать сообщения в ящике, даже если сообщение WhatsApp уже
есть в сопоставлении беседы.
cli.cmd.messages.pull.option.batch: Количество сообщений в каждом пакете.
cli.cmd.messages.pull.option.progress:
Сообщать о прогрессе каждые N сообщений. Значение 0 отключает отчёты.
cli.cmd.messages.pull.option.no-dm:
Пропускать личные сообщения при загрузке истории.
cli.cmd.messages.pull.option.groups:
Включать группы WhatsApp при загрузке истории.
cli.cmd.messages.pull.option.channels:
Включать каналы WhatsApp при загрузке истории.
cli.cmd.messages.pull.option.status:
Включать статусы WhatsApp при загрузке истории.
cli.cmd.messages.pull.option.broadcast:
Включать списки рассылок при загрузке истории.
cli.cmd.messages.pull.option.media:
Скачивать медиафайлы (изображения, видео, документы) при загрузке истории.
cli.cmd.messages.pull.option.pause:
Поставить задачу загрузки сообщений в очередь на паузу и остановить обработку
новых сообщений WhatsApp до завершения.
cli.cmd.messages.pull.option.resolve-conversations:
Отмечает беседы как завершённые после обработки всей истории сообщений.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ Невозможно использовать `--pause`, потому что команда **queue** отключена в
этой конфигурации.
cli.cmd.queue.description: Управление очередями.
cli.cmd.queue.summary: Действия с очередями.
cli.cmd.queue.action.description: Выберите действие.
cli.cmd.queue.status.description: Показать состояние очереди.
cli.cmd.queue.argument.name:
'Название очереди: `inbox` или `whatsapp` (необязательно).'
cli.cmd.queue.start.description: Запустить очередь по имени.
cli.cmd.queue.stop.description: Остановить очередь по имени.
cli.cmd.queue.paused:
⚪🧩 Очереди поставлены на паузу. Отправьте `queue status`, чтобы проверить
состояние.
cli.cmd.queue.resumed:
🟢🧩 Очереди возобновлены. Отправьте `queue status`, чтобы проверить
состояние.
cli.cmd.queue.status.result: >-
📊 Состояние очередей:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ На
паузе{{/paused}}{{^paused}}🟢 Активна{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Указывает, как долго пытаться скачать медиа для каждого сообщения.
cli.cmd.messages.pull.argument.end:
Задаёт конец окна загрузки, например `2d` — до двух дней назад.
cli.cmd.messages.pull.argument.start:
Задаёт начало окна загрузки, например `1d` — с одного дня назад (по умолчанию
`0d`, то есть сейчас).
cli.cmd.messages.pull.already-running: >-
⏳ Задача **Pull Messages** уже выполняется для **{{{chat}}}**. Дождитесь
завершения или остановите её перед запуском новой.
Отправьте `messages status` для деталей.
cli.cmd.messages.status.not-found:
ℹ️ Активная задача **Pull Messages** не найдена. Запустите её командой
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
Чаты WhatsApp для выбранного периода не найдены.
cli.cmd.contacts.pull.option.progress:
Сообщать о прогрессе каждые N контактов. Значение 0 отключает отчёты.
cli.cmd.contacts.pull.option.lids:
Включать контакты WhatsApp LID (анонимные контакты) в синхронизацию.
task.contacts.completed:
👤✅ Контакты завершены → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ Загружаем сообщения 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Отправьте "{{{prefix}}}messages status", чтобы увидеть подробности.
task.messages.progress:
'⏳ Сообщения {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Сообщения {{{chat}}} за {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Сообщения **{{{chat}}}** за период ⏱️ **{{{period}}}**. ⚙️ Статус задачи: **{{state}}**.
🗨️ Сообщения:
- 🆕 Создано: **{{{progress.ok}}}**
- 🟢 Уже есть: {{{progress.exists}}}
- ⚪ Пропущено: {{{progress.ignored}}}
- 🔴 Ошибки: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Самое раннее: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Самое позднее: **{{{messages.last}}}**
{{/messages.last}}
💬 Чаты WhatsApp:
- 🆕 Новые сообщения в **{{{chats}}}** чатах
{{#options.resolveConversations}}
🗣️ Беседы:
- 🟢 Закрыто: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Ошибки: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Подробности:
[{{{details.text}}}]({{{details.url}}})
+349 -37
View File
@@ -1,4 +1,4 @@
# Turkish translations for ChatWoot integration
# Turkish translations for WhatsApp inbox integration
locale.name: '🇹🇷 Türkçe (Turkish)'
# Temel yerel kodu - geçerli bir BCP 47 yerel kodu olmalı
@@ -6,23 +6,28 @@ locale.name: '🇹🇷 Türkçe (Turkish)'
# WAHA bunu sayı, para birimi ve tarih/saat gösterimi için kullanır
locale.base: 'tr-TR'
app.inbox.contact.name: 'WhatsApp Entegrasyonu (WAHA)'
app.inbox.contact.name: 'WhatsApp Entegrasyonu'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString için "options"
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: tr-TR
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Istanbul'
Today: 'Bugün'
Yesterday: 'Dün'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: WhatsApp'tan mesaj kaldırılamadı
job.scheduled.error.header: Zamanlanmış görev yürütülemedi
message.from.whatsapp: |-
{{{text}}}
*📱 WhatsApp'tan gönderildi*
📱 *WhatsApp'tan gönderildi*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 API aracılığıyla gönderildi*
📱 *API aracılığıyla gönderildi*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Bu mesaj WhatsApp'ta silindi*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Yayın Listesi'
whatsapp.contact.status.name: 'Durum'
app.connected.message: |-
🔗 **BAĞLANDI: WhatsApp Oturumu ChatWoot Gelen Kutusuna**
🔗 **BAĞLANDI: WhatsApp Oturumu Gelen Kutusuna**
✅ WhatsApp oturumu **{{{name}}}** bu ChatWoot Gelen Kutusuna başarıyla bağlandı.
✅ WhatsApp oturumu **{{{name}}}** bu gelen kutusuna başarıyla bağlandı.
Bu gelen kutusu artık **WhatsApp Hesabınıza** bağlı. Mesajları doğrudan buradan gönderip alabilirsiniz — tıpkı WhatsApp kullanır gibi, ancak ChatWoot içinde.
Bu gelen kutusu artık **WhatsApp Hesabınıza** bağlı. Mesajları doğrudan buradan gönderip alabilirsiniz — tıpkı WhatsApp kullanır gibi, ancak çalışma alanınız içinde.
Ayrıca bu gelen kutusuna bağlı WhatsApp oturumunu da yönetebilirsiniz. Kullanılabilir eylemleri görmek için, bu sohbete **help** gönderin.
@@ -104,9 +109,9 @@ app.connected.message: |-
Hadi başlayalım! 🚀
app.disconnected.message: |-
🔌 **BAĞLANTI KESİLDİ: WhatsApp Oturumu ChatWoot Gelen Kutusundan**
🔌 **BAĞLANTI KESİLDİ: WhatsApp Oturumu Gelen Kutusundan**
❌ WhatsApp oturumu **{{{name}}}** bu ChatWoot Gelen Kutusundan bağlantısı kesildi.
❌ WhatsApp oturumu **{{{name}}}** bu gelen kutusundan bağlantısı kesildi.
Artık WhatsApp'tan mesaj gönderemez veya alamazsınız.
@@ -117,34 +122,156 @@ app.updated.message: |-
✅ WhatsApp oturumu **{{{name}}}** için ayarlar güncellendi.
app.commands.list: |-
Aşağıdaki komutlardan birini göndererek **WhatsApp Entegrasyonu**nu yönetebilirsiniz:
cli.usage.arguments: |-
[eylem]
🖥️ **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.
4. **stop** - oturumu durdurun.
5. **logout** - oturumdan çıkış yapın, böylece QR kodu tarayabilir ve oturumu onarabilirsiniz.
6. **qr** - oturum için QR kodu alın.
7. **screenshot** - oturumun ekran görüntüsünü alın (yalnızca **WEBJS** motorunda kullanılabilir).
8. **help** - kullanılabilir komutlar.
cli.usage.command: |-
[komut]
{{#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}}
cli.usage.options: |-
[seçenekler]
Bu konuşmaya bir **komut** (büyük/küçük harf duyarsız) veya **komut numarası** gönderin.
app.commands.server.disabled: ⛔ **Sunucu komutları devre dışı**.
cli.cmd.disabled: ⛔ **`{{command}}` komutu devre dışı**.
Lütfen yöneticinizle iletişime geçin.
Kullanılabilir komutların listesini almak için **help** gönderin.
cli.cmd.root.description: |-
**WhatsApp entegrasyonunuzu** yönetin!
cli.cmd.root.sub.session: |-
🖥️ **Oturum**:
cli.cmd.root.sub.server: |-
🖥️ **Sunucu**:
cli.cmd.root.sub.sync: |-
🔄️ **Eşitleme**:
cli.cmd.session.status.description: |-
Geçerli oturum durumunu alın.
cli.cmd.session.restart.description: |-
Oturumu yeniden başlatın.
cli.cmd.session.start.description: |-
Oturumu başlatın.
cli.cmd.session.stop.description: |-
Oturumu durdurun.
cli.cmd.session.logout.description: |-
Bağlantıyı yeniden kurmak için oturumdan çıkın ve yeni bir QR kodu tarayın.
cli.cmd.session.qr.description: |-
Oturumun QR kodunu gösterin.
cli.cmd.session.screenshot.description: |-
Oturumun ekran görüntüsünü alın (yalnızca **WEBJS** motoru ile kullanılabilir).
cli.cmd.contacts.summary: |-
WhatsApp kişilerini içe aktarın.
cli.cmd.contacts.description: |-
👤 `contacts pull` komutunu çalıştırarak bir eşitleme başlatın veya `contacts status` ile ilerlemeyi kontrol edin.
cli.cmd.contacts.action.description: |-
`start` ile kişi çekmeyi başlatın veya `status` ile etkin görevi kontrol edin.
cli.cmd.contacts.pull.description: |-
WhatsApp kişilerini bu gelen kutusuna çekmeye başlayın.
cli.cmd.contacts.pull.option.batch: |-
Her partide çekilecek kişi sayısı.
cli.cmd.contacts.pull.option.delay-contact: |-
Avatarları çekerken limitlere takılmamak için her kişi arasında bekleme süresi.
cli.cmd.contacts.pull.option.delay-batch: |-
Kişi partileri arasındaki bekleme süresi.
cli.cmd.contacts.pull.option.avatar: |-
Kişi avatarlarının nasıl çekileceğini seçin: `skip`, `if-missing` veya `update`.
cli.cmd.contacts.pull.option.groups: |-
WhatsApp grup kişilerini senkronizasyona dahil et.
cli.cmd.contacts.pull.option.no-attributes: |-
Kişi zaten varsa özelliklerini çekmeyin (süreci hızlandırır).
cli.cmd.contacts.pull.already-running: |-
⏳ **Pull Contacts** görevi zaten çalışıyor. Bitmesini bekleyin veya yenisini başlatmadan önce durdurun.
Ayrıntılar için `contacts status` gönderin.
cli.cmd.options.job.attempts: |-
Görevi başarısız saymadan önce yapılacak yeniden deneme sayısı.
cli.cmd.options.job.timeout: |-
Görev yeniden denenmeden önce çalışmasına izin verilen en uzun süre (`30s`, `5m`, `1h` gibi değerleri destekler).
cli.cmd.contacts.status.description: |-
Devam eden kişi çekme görevinin durumunu gösterin.
cli.cmd.contacts.status.not-found: |-
ℹ️ Aktif bir **Pull Contacts** görevi bulunamadı. `contacts pull` ile bir tane başlatın.
cli.cmd.server.description: |-
Sunucuyu yönetin.
cli.cmd.server.status.description: |-
Sunucunun sürümünü ve durumunu gösterin.
cli.cmd.server.reboot.description: |-
Sunucuyu yeniden başlatın.
cli.cmd.server.reboot.option.force: |-
Anında yeniden başlatmayı zorlayın.
cli.help.usage.title: |-
Kullanım: Komutu aşağıdaki formatta sohbete gönderin:
cli.help.commands.defaultGroup: |-
**Komutlar:**
cli.help.arguments.title: |-
**Argümanlar:**
cli.help.options.defaultGroup: |-
**Seçenekler:**
cli.help.globalOptions.title: |-
**Genel seçenekler:**
task.contacts.status: |-
👤 Kişi çekme: **{{state}}**
- ✅ Başarılı: {{{progress.ok}}} kişi
- ⚪ Atlanan: {{{progress.skipped}}} kişi
- ⚠️ Hatalar: {{{progress.errors}}} kişi
{{#job}}
⌛ Zaman çizelgesi:
- Sıraya alındı: {{{timestamp}}}
{{#processedOn}}
- Başladı: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Bitti: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Ayrıntılar:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Kişiler çekiliyor...
task.contacts.progress: |-
Kişi çekme: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Kullanılabilir komutların listesini almak için **help** gönderin.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Bu ChatWoot Gelen Kutusu **WhatsApp Hesabına** bağlı ve mesaj gönderip almaya hazır!
✅ Bu gelen kutusu **WhatsApp Hesabınıza** bağlı ve mesaj gönderip almaya hazır!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **Bu ChatWoot Gelen Kutusu WhatsApp Hesabına bağlı ancak mesaj gönderip almaya hazır DEĞİL!**
⚠️ **Bu 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: |-
⚠️ **WhatsApp Hesabını bu ChatWoot Gelen Kutusuna bağlamak için telefonunuzda QR kodu tarayın!**
⚠️ **WhatsApp Hesabını bu gelen kutusuna bağlamak için telefonunuzda QR kodu tarayın!**
app.server.version.and.status: |-
**Sürüm:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Bu mesaj türü ChatWoot’ta desteklenmiyor.**
⚠️ **Bu mesaj türü bu gelen kutusunda desteklenmiyor.**
📱 Görüntülemek için lütfen **WhatsApp’ı** açın.
Ayrıntılar: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Albüm Mesajı Alındı**
{{#totalCount}}
**{{totalCount}}** medya dosyası içeren bir albüm alındı.
{{#expectedImageCount}}
📸 Resimler: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Videolar: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Medya dosyaları aşağıdaki mesajlarda teslim edilecek.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK İLANI**
@@ -352,3 +522,145 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Müşteri Mesajı:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Bilinmiyor
ERROR: Hata
PENDING: Gönderiliyor
SERVER: Sunucu
DEVICE: Cihaz
READ: Okundu
PLAYED: Oynatıldı
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **Mesajda medya var ancak indirilemedi**
📱 Görüntülemek için lütfen **WhatsApp** uygulamasını açın.
{{^content}}
Detaylar: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Kuyruk**:'
cli.cmd.messages.summary: WhatsApp mesajlarını çek.
cli.cmd.messages.description:
💬 Mesaj senkronizasyonu başlatmak için `messages pull`, ilerlemeyi görmek
için `messages status` komutunu çalıştırın.
cli.cmd.messages.action.description:
'`pull` ile mesaj çekmeyi başlatın, `status` ile çalışan işi inceleyin.'
cli.cmd.messages.pull.option.chat:
Çekimi belirli bir WhatsApp sohbet kimliğiyle sınırlandırır.
cli.cmd.messages.pull.option.force:
WhatsApp mesajı konuşma eşlemesinde olsa bile gelen kutusunun mesaj
oluşturmasını zorlar.
cli.cmd.messages.pull.option.batch: Her partide çekilecek mesaj sayısı.
cli.cmd.messages.pull.option.progress:
Her N mesajda ilerlemeyi bildirir. 0 değeri raporları kapatır.
cli.cmd.messages.pull.option.no-dm: Geçmişi çekerken direkt mesajları atlar.
cli.cmd.messages.pull.option.groups:
Geçmişi çekerken WhatsApp grup sohbetlerini dahil eder.
cli.cmd.messages.pull.option.channels:
Geçmişi çekerken WhatsApp Kanallarını dahil eder.
cli.cmd.messages.pull.option.status:
Geçmişi çekerken WhatsApp durum güncellemelerini dahil eder.
cli.cmd.messages.pull.option.broadcast:
Geçmişi çekerken yayın listelerini dahil eder.
cli.cmd.messages.pull.option.media:
Geçmişi çekerken medya eklerini (görseller, videolar, dosyalar) indirir.
cli.cmd.messages.pull.option.pause:
Mesaj çekme işini duraklatılmış olarak kuyruklar ve tamamlanana kadar yeni
WhatsApp mesajlarının işlenmesini durdurur.
cli.cmd.messages.pull.option.resolve-conversations:
Tüm geçmiş mesajlar işlendiğinde konuşmaları çözüldü olarak işaretler.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ `--pause` kullanılamaz çünkü **queue** komutu bu yapılandırmada devre dışı.
cli.cmd.queue.description: Kuyrukları yönetin.
cli.cmd.queue.summary: Kuyruklarda işlemler gerçekleştirin.
cli.cmd.queue.action.description: Bir eylem seçin.
cli.cmd.queue.status.description: Bir kuyruğun durumunu gösterir.
cli.cmd.queue.argument.name:
'Kuyruk adı: `inbox` veya `whatsapp` (isteğe bağlı).'
cli.cmd.queue.start.description: Adı verilen kuyruğu başlatın.
cli.cmd.queue.stop.description: Adı verilen kuyruğu durdurun.
cli.cmd.queue.paused:
⚪🧩 Kuyruklar duraklatıldı. Durumu kontrol etmek için `queue status`
gönderin.
cli.cmd.queue.resumed:
🟢🧩 Kuyruklar devam ettirildi. Durumu kontrol etmek için `queue status`
gönderin.
cli.cmd.queue.status.result: >-
📊 Kuyruk durumu:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪
Duraklatıldı{{/paused}}{{^paused}}🟢 Çalışıyor{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Her mesaj için medya indirme süresini belirler.
cli.cmd.messages.pull.argument.end:
Çekim penceresinin nerede biteceğini belirler; örneğin `2d` iki gün öncesinde
durur.
cli.cmd.messages.pull.argument.start:
Çekim penceresinin nerede başlayacağını belirler; örneğin `1d` bir gün
öncesinden başlar (varsayılan `0d`, yani şimdi).
cli.cmd.messages.pull.already-running: >-
⏳ **Pull Messages** işi zaten **{{{chat}}}** için çalışıyor. Bitmesini
bekleyin veya yenisini başlatmadan önce durdurun.
Ayrıntılar için `messages status` gönderin.
cli.cmd.messages.status.not-found:
ℹ️ Aktif **Pull Messages** işi bulunamadı. `messages pull` ile bir tane
başlatın.
cli.cmd.messages.pull.no-chats-found:
Belirtilen dönemde çekilecek WhatsApp sohbeti bulunamadı.
cli.cmd.contacts.pull.option.progress:
Her N kişide ilerlemeyi bildirir. 0 değeri raporları kapatır.
cli.cmd.contacts.pull.option.lids:
Senkronizasyona WhatsApp LID (anonim) kişilerini dahil eder.
task.contacts.completed:
👤✅ Kişiler tamamlandı → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started: ⏳ Mesajlar çekiliyor 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Daha fazla ayrıntı için "{{{prefix}}}messages status" gönderin.
task.messages.progress:
'⏳ Mesajlar {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Mesajlar {{{chat}}} {{{period}}} içinde → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
**{{{chat}}}** sohbetinin mesajları ⏱️ **{{{period}}}** döneminde. ⚙️ İş durumu: **{{state}}**.
🗨️ Mesajlar:
- 🆕 Oluşturulan: **{{{progress.ok}}}**
- 🟢 Zaten mevcut: {{{progress.exists}}}
- ⚪ Yok sayılan: {{{progress.ignored}}}
- 🔴 Hatalar: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ En eski: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ En yeni: **{{{messages.last}}}**
{{/messages.last}}
💬 WhatsApp sohbetleri:
- 🆕 **{{{chats}}}** sohbette yeni mesajlar
{{#options.resolveConversations}}
🗣️ Konuşmalar:
- 🟢 Çözüldü: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Hatalar: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Detaylar:
[{{{details.text}}}]({{{details.url}}})
+350 -38
View File
@@ -1,4 +1,4 @@
# Ukrainian translations for ChatWoot integration
# Ukrainian translations for WhatsApp inbox integration
locale.name: '🇺🇦 Українська (Ukrainian)'
# Базовий код локалі - має бути чинною локаллю BCP 47
@@ -6,23 +6,28 @@ locale.name: '🇺🇦 Українська (Ukrainian)'
# WAHA використовує його для форматування чисел, валют і дати/часу
locale.base: 'uk-UA'
app.inbox.contact.name: 'Інтеграція WhatsApp (WAHA)'
app.inbox.contact.name: 'Інтеграція WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "параметри" з date.toLocaleString
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options
datetime:
weekday: short
locales: uk-UA
day: 2-digit
month: 2-digit
month: short
year: numeric
hour: 2-digit
minute: 2-digit
hour12: false
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#timezonename
timeZoneName: shortGeneric
timeZoneName: short
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# timeZone: 'Europe/Kyiv'
Today: 'Сьогодні'
Yesterday: 'Вчора'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -51,14 +56,14 @@ whatsapp.message.removing.error: Не вдалося видалити повід
job.scheduled.error.header: Заплановане завдання не вдалося виконати
message.from.whatsapp: |-
{{{text}}}
*📱 Надіслано з WhatsApp*
📱 *Надіслано з WhatsApp*
{{{text}}}
message.from.api: |-
{{{text}}}
*📱 Надіслано через API*
📱 *Надіслано через API*
{{{text}}}
message.removed.in.whatsapp: |-
❌ *Це повідомлення було видалено у WhatsApp*
@@ -86,11 +91,11 @@ whatsapp.contact.broadcast.suffix: 'Список розсилки'
whatsapp.contact.status.name: 'Статус'
app.connected.message: |-
🔗 **ПІДКЛЮЧЕНО: Сесія WhatsApp до Вхідних ChatWoot**
🔗 **ПІДКЛЮЧЕНО: Сесія WhatsApp до вхідних**
✅ Сесія WhatsApp **{{{name}}}** була успішно підключена до цих Вхідних ChatWoot.
✅ Сесія WhatsApp **{{{name}}}** була успішно підключена до цих вхідних.
Ці вхідні тепер пов'язані з вашим **Обліковим записом WhatsApp**. Ви можете надсилати та отримувати повідомлення безпосередньо звідси — так само, як використовуючи WhatsApp, але всередині ChatWoot.
Ці вхідні тепер пов'язані з вашим **Обліковим записом WhatsApp**. Ви можете надсилати та отримувати повідомлення безпосередньо звідси — так само, як використовуючи WhatsApp, але у своєму робочому просторі.
Ви також можете керувати сесією WhatsApp, пов'язаною з цими вхідними. Щоб переглянути доступні дії, надішліть **help** у цей чат.
@@ -104,9 +109,9 @@ app.connected.message: |-
Давайте почнемо! 🚀
app.disconnected.message: |-
🔌 **ВІДКЛЮЧЕНО: Сесія WhatsApp від Вхідних ChatWoot**
🔌 **ВІДКЛЮЧЕНО: Сесія WhatsApp від вхідних**
❌ Сесія WhatsApp **{{{name}}}** була відключена від цих Вхідних ChatWoot.
❌ Сесія WhatsApp **{{{name}}}** була відключена від цих вхідних.
Ви більше не зможете надсилати або отримувати повідомлення з WhatsApp.
@@ -117,34 +122,156 @@ app.updated.message: |-
✅ Налаштування для сесії WhatsApp **{{{name}}}** були оновлені.
app.commands.list: |-
Ви можете керувати **Інтеграцією WhatsApp**, надіславши одну з наступних команд:
cli.usage.arguments: |-
[дія]
🖥️ **Сесія**:
1. **status** - отримати поточний статус сесії.
2. **restart** - перезапустити сесію - це спробує повторно підключити ваш обліковий запис WhatsApp.
3. **start** - запустити сесію.
4. **stop** - зупинити сесію.
5. **logout** - вийти з сесії, щоб ви могли відсканувати QR-код і відновити сесію.
6. **qr** - отримати QR-код для сесії.
7. **screenshot** - отримати знімок екрану сесії (доступно лише в двигуні **WEBJS**).
8. **help** - доступні команди.
cli.usage.command: |-
[команда]
{{#commands.server}}
⚙️ Сервер:
- **server status** - отримати інформацію про версію та статус сервера.
- **server reboot** - перезавантажити сервер плавно.
- **server reboot force** - примусово перезавантажити сервер.
{{/commands.server}}
cli.usage.options: |-
[опції]
Надішліть **команду** (без урахування регістру) або **номер команди** у цю розмову.
cli.cmd.disabled: ⛔ **Команду `{{command}}` вимкнено**.
app.commands.server.disabled: ⛔ **Команди сервера вимкнені**.
Будь ласка, зв’яжіться з вашим адміністратором.
Будь ласка, зверніться до свого адміністратора.
Надішліть **help**, щоб отримати список доступних команд.
cli.cmd.root.description: |-
Керуйте своєю **інтеграцією WhatsApp**!
cli.cmd.root.sub.session: |-
🖥️ **Сесія**:
cli.cmd.root.sub.server: |-
🖥️ **Сервер**:
cli.cmd.root.sub.sync: |-
🔄️ **Синхронізація**:
cli.cmd.session.status.description: |-
Отримайте поточний статус сесії.
cli.cmd.session.restart.description: |-
Перезапустіть сесію.
cli.cmd.session.start.description: |-
Запустіть сесію.
cli.cmd.session.stop.description: |-
Зупиніть сесію.
cli.cmd.session.logout.description: |-
Вийдіть із сесії, щоб відсканувати новий QR-код і відновити з'єднання.
cli.cmd.session.qr.description: |-
Показати QR-код сесії.
cli.cmd.session.screenshot.description: |-
Зробіть знімок екрана сесії (доступно лише з рушієм **WEBJS**).
cli.cmd.contacts.summary: |-
Отримати контакти WhatsApp.
cli.cmd.contacts.description: |-
👤 Запустіть `contacts pull`, щоб почати синхронізацію, або `contacts status`, щоб відстежити прогрес.
cli.cmd.contacts.action.description: |-
Використайте `start`, щоб почати отримання контактів, або `status`, щоб перевірити активне завдання.
cli.cmd.contacts.pull.description: |-
Почніть імпортувати контакти WhatsApp у ці вхідні.
cli.cmd.contacts.pull.option.batch: |-
Кількість контактів у кожній партії.
cli.cmd.contacts.pull.option.delay-contact: |-
Затримка між кожним контактом, щоб уникнути обмежень під час завантаження аватарів.
cli.cmd.contacts.pull.option.delay-batch: |-
Затримка між партіями контактів.
cli.cmd.contacts.pull.option.avatar: |-
Виберіть, як завантажувати аватари контактів: `skip`, `if-missing` чи `update`.
cli.cmd.contacts.pull.option.groups: |-
Додати контакти груп WhatsApp до синхронізації.
cli.cmd.contacts.pull.option.no-attributes: |-
Не завантажуйте атрибути контакту, якщо він уже існує (це пришвидшує процес).
cli.cmd.contacts.pull.already-running: |-
⏳ Завдання **Pull Contacts** вже виконується. Дочекайтеся завершення або зупиніть його перед запуском нового.
Надішліть `contacts status`, щоб отримати деталі.
cli.cmd.options.job.attempts: |-
Кількість повторних спроб перед позначенням завдання як невдалого.
cli.cmd.options.job.timeout: |-
Максимальний час виконання перед повторною спробою (підтримуються значення `30s`, `5m`, `1h`).
cli.cmd.contacts.status.description: |-
Показати статус поточного завдання отримання.
cli.cmd.contacts.status.not-found: |-
ℹ️ Активне завдання **Pull Contacts** не знайдено. Запустіть його командою `contacts pull`.
cli.cmd.server.description: |-
Керуйте сервером.
cli.cmd.server.status.description: |-
Показати версію та статус сервера.
cli.cmd.server.reboot.description: |-
Перезавантажте сервер.
cli.cmd.server.reboot.option.force: |-
Примусово виконайте негайне перезавантаження.
cli.help.usage.title: |-
Використання: надішліть у розмову команду у такому форматі:
cli.help.commands.defaultGroup: |-
**Команди:**
cli.help.arguments.title: |-
**Аргументи:**
cli.help.options.defaultGroup: |-
**Опції:**
cli.help.globalOptions.title: |-
**Глобальні опції:**
task.contacts.status: |-
👤 Отримання контактів: **{{state}}**
- ✅ Успішно: {{{progress.ok}}} контактів
- ⚪ Пропущено: {{{progress.skipped}}} контактів
- ⚠️ Помилки: {{{progress.errors}}} контактів
{{#job}}
⌛ Хронологія:
- У черзі: {{{timestamp}}}
{{#processedOn}}
- Початок: {{{processedOn}}}
{{/processedOn}}
{{#finishedOn}}
- Завершення: {{{finishedOn}}}
{{/finishedOn}}
{{/job}}
{{#error}}
Деталі:
[{{{details.text}}}]({{{details.url}}})
{{/error}}
task.contacts.started: |-
Отримання контактів...
task.contacts.progress: |-
Отримання контактів: ✅ {{progress.ok}}, ⚪ {{progress.skipped}}, ⚠️ {{progress.errors}}
app.help.reminder: |-
Надішліть **help**, щоб отримати список доступних команд.
@@ -158,17 +285,17 @@ app.session.current.status: |-
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |-
✅ Ці Вхідні ChatWoot пов'язані з **Обліковим записом WhatsApp** і готові надсилати та отримувати повідомлення!
✅ Ці вхідні пов'язані з вашим **Обліковим записом WhatsApp** і готові надсилати та отримувати повідомлення!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |-
⚠️ **Ці Вхідні ChatWoot пов'язані з Обліковим записом WhatsApp, але НЕ готові надсилати та отримувати повідомлення!**
⚠️ **Ці вхідні пов'язані з Обліковим записом WhatsApp, але НЕ готові надсилати та отримувати повідомлення!**
Будь ласка, перевірте статус сесії та спробуйте перезапустити її або вийдіть і відскануйте QR-код знову.
app.session.scan.qr.code: |-
⚠️ **Відскануйте QR-код на вашому телефоні, щоб пов'язати Обліковий запис WhatsApp з цими Вхідними ChatWoot!**
⚠️ **Відскануйте QR-код на вашому телефоні, щоб пов'язати Обліковий запис WhatsApp з цими вхідними!**
app.server.version.and.status: |-
**Версія:**
@@ -283,6 +410,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
@@ -333,11 +488,26 @@ whatsapp.to.chatwoot.message.pix: |-
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Цей тип повідомлення не підтримується в ChatWoot.**
⚠️ **Цей тип повідомлення не підтримується в цих вхідних.**
📱 Будь ласка, відкрийте **WhatsApp**, щоб переглянути.
Подробиці: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.album: |-
📷 **Отримано повідомлення з альбомом**
{{#totalCount}}
Отримано альбом з **{{totalCount}}** медіафайлом(ами).
{{#expectedImageCount}}
📸 Зображень: **{{expectedImageCount}}**
{{/expectedImageCount}}
{{#expectedVideoCount}}
🎥 Відео: **{{expectedVideoCount}}**
{{/expectedVideoCount}}
Медіафайли будуть доставлені в наступних повідомленнях.
{{/totalCount}}
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **РЕКЛАМА FACEBOOK**
@@ -352,3 +522,145 @@ whatsapp.to.chatwoot.message.facebook.ad: |-
---
💬 **Повідомлення клієнта:**
{{{payload.body}}}
whatsapp.history.message.wrapper: |-
{{{content}}}
`{{#history}}📥{{/history}}🗓️ {{{timestamp}}}`
UNKNOWN: Невідомо
ERROR: Помилка
PENDING: Надсилається
SERVER: Сервер
DEVICE: Пристрій
READ: Прочитано
PLAYED: Прослухано
whatsapp.to.chatwoot.message.has.media.no.media: |-
{{#content}}
{{{content}}}
{{/content}}
🖼️⚠️ **У повідомленні є медіа, але не вдалося його завантажити**
📱 Відкрийте **WhatsApp**, щоб переглянути.
{{^content}}
Деталі: [{{{details.text}}}]({{{details.url}}})
{{/content}}
cli.cmd.root.sub.queue: '🧩 **Черга**:'
cli.cmd.messages.summary: Отримати повідомлення WhatsApp.
cli.cmd.messages.description:
💬 Виконайте `messages pull`, щоб запустити синхронізацію, або `messages
status`, щоб переглянути прогрес.
cli.cmd.messages.action.description:
'`pull` — щоб почати отримання, `status` — щоб переглянути активне завдання.'
cli.cmd.messages.pull.option.chat:
Обмежити отримання конкретним ідентифікатором чату WhatsApp.
cli.cmd.messages.pull.option.force:
Примусити скриньку створювати повідомлення, навіть якщо повідомлення WhatsApp
уже є у відповідності розмови.
cli.cmd.messages.pull.option.batch: Кількість повідомлень у кожній партії.
cli.cmd.messages.pull.option.progress:
Повідомляти про прогрес кожні N повідомлень. Значення 0 вимикає звіти.
cli.cmd.messages.pull.option.no-dm:
Пропускати приватні повідомлення під час отримання історії.
cli.cmd.messages.pull.option.groups:
Включати групи WhatsApp під час отримання історії.
cli.cmd.messages.pull.option.channels:
Включати канали WhatsApp під час отримання історії.
cli.cmd.messages.pull.option.status:
Включати статуси WhatsApp під час отримання історії.
cli.cmd.messages.pull.option.broadcast:
Включати списки розсилки під час отримання історії.
cli.cmd.messages.pull.option.media:
Завантажувати медіафайли (зображення, відео, файли) під час отримання історії.
cli.cmd.messages.pull.option.pause:
Поставити завдання отримання повідомлень у чергу на паузі й зупинити обробку
нових WhatsApp до завершення.
cli.cmd.messages.pull.option.resolve-conversations:
Позначає розмови як завершені після обробки всієї історії повідомлень.
cli.cmd.messages.pull.error.pause-no-queue:
⛔ Неможливо використати `--pause`, оскільки команда **queue** вимкнена для
цієї конфігурації.
cli.cmd.queue.description: Керування чергами.
cli.cmd.queue.summary: Дії з чергами.
cli.cmd.queue.action.description: Оберіть дію.
cli.cmd.queue.status.description: Показати стан черги.
cli.cmd.queue.argument.name:
'Назва черги: `inbox` або `whatsapp` (необов’язково).'
cli.cmd.queue.start.description: Запустити чергу за назвою.
cli.cmd.queue.stop.description: Зупинити чергу за назвою.
cli.cmd.queue.paused:
⚪🧩 Черги призупинені. Надішліть `queue status`, щоб перевірити стан.
cli.cmd.queue.resumed:
🟢🧩 Черги поновлено. Надішліть `queue status`, щоб перевірити стан.
cli.cmd.queue.status.result: >-
📊 Стан черг:
{{#queues}}
- **{{name}}**: {{#locked}}🔒{{/locked}}{{#paused}}⚪ На
паузі{{/paused}}{{^paused}}🟢 Активна{{/paused}}
{{/queues}}
cli.cmd.messages.pull.option.timeout-media:
Визначає, як довго намагатися завантажувати медіа для кожного повідомлення.
cli.cmd.messages.pull.argument.end:
Вказує, де закінчується вікно отримання; наприклад, `2d` — два дні тому.
cli.cmd.messages.pull.argument.start:
Вказує, де починається вікно; наприклад, `1d` — один день тому (за
замовчуванням `0d`, тобто зараз).
cli.cmd.messages.pull.already-running: >-
⏳ Завдання **Pull Messages** уже виконується для **{{{chat}}}**. Дочекайтеся
завершення або зупиніть його перед запуском нового.
Надішліть `messages status`, щоб отримати деталі.
cli.cmd.messages.status.not-found:
ℹ️ Активне завдання **Pull Messages** не знайдено. Запустіть його командою
`messages pull`.
cli.cmd.messages.pull.no-chats-found:
Не знайдено чатів WhatsApp для вибраного періоду.
cli.cmd.contacts.pull.option.progress:
Повідомляти про прогрес кожні N контактів. Значення 0 вимикає звіти.
cli.cmd.contacts.pull.option.lids:
Включати анонімні контакти WhatsApp LID у синхронізацію.
task.contacts.completed:
👤✅ Контакти завершено → 🆕 {{progress.created}}, 🔄 {{progress.updated}}, ⚪
{{progress.skipped}}, 🔴 {{progress.errors}}, 🖼️ {{progress.avatar.updated}}
task.messages.started:
⏳ Отримуємо повідомлення 💬 {{{chat}}} ⏱️ {{{period}}}...
task.messages.details:
Надішліть "{{{prefix}}}messages status", щоб побачити деталі.
task.messages.progress:
'⏳ Повідомлення {{{chat}}}: 🆕 {{progress.ok}}, 🟢 {{progress.exists}}, ⚪
{{progress.ignored}}, 🔴 {{progress.errors}}{{#messages.last}} | 📅
{{{messages.last}}}{{/messages.last}}'
task.messages.completed:
✅ Повідомлення {{{chat}}} за {{{period}}} → 🆕 {{progress.ok}}, 🟢
{{progress.exists}}, ⚪ {{progress.ignored}}, 🔴 {{progress.errors}}
task.messages.status: |-
Повідомлення **{{{chat}}}** у період ⏱️ **{{{period}}}**. ⚙️ Стан завдання: **{{state}}**.
🗨️ Повідомлення:
- 🆕 Створено: **{{{progress.ok}}}**
- 🟢 Уже є: {{{progress.exists}}}
- ⚪ Пропущено: {{{progress.ignored}}}
- 🔴 Помилки: {{{progress.errors}}}
{{#messages.first}}
- 🗓️ Найраніше: **{{{messages.first}}}**
{{/messages.first}}
{{#messages.last}}
- 🗓️ Найпізніше: **{{{messages.last}}}**
{{/messages.last}}
💬 Чати WhatsApp:
- 🆕 Нові повідомлення в **{{{chats}}}** чатах
{{#options.resolveConversations}}
🗣️ Розмови:
- 🟢 Закрито: **{{{progress.conversationResolve.success}}}**
{{#progress.conversationResolve.error}}
- 🔴 Помилки: **{{{progress.conversationResolve.error}}}**
{{/progress.conversationResolve.error}}
{{/options.resolveConversations}}
Деталі:
[{{{details.text}}}]({{{details.url}}})
Loaded 100 of 188 files, more files were not shown because too many files have changed in this diff. Show more