Compare commits

...
49 Commits
Author SHA1 Message Date
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
devlikepro 5cfb53c367 [core] 2025.10.1
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-10-12 18:45:57 +07:00
devlikepro 9466407b8a [core] ChatWoot - update last seen (on message.ack)
closes #1436
fixes #1319
2025-10-12 18:45:56 +07:00
devlikepro 4b65f0c462 [core] ChatWoot - up to v4.6.0 2025-10-12 18:45:56 +07:00
devlikepro 23504b35c8 [core] ChatWoot - remove /n at the end of any templated 2025-10-12 18:45:55 +07:00
devlikepro dd6ec4ca3a [core] ChatWoot - Pix fixes 2025-10-12 18:45:55 +07:00
devlikepro 8235e5a111 [core] ChatWoot - i18n - pt enable timeZone Sao Paulo 2025-10-12 18:45:55 +07:00
devlikepro 55499a6144 [core] ChatWoot - i18n - datetime 2025-10-12 18:45:54 +07:00
devlikepro 9dbc4a6085 [core] ChatWoot - i18n - poll, event, pix 2025-10-12 18:45:54 +07:00
devlikepro f7c6ce847c [core] ChatWoot - i18n - poll no empty line 2025-10-12 18:45:53 +07:00
devlikepro 84eed00415 [core] ChatWoot - i18n - location url (not URL) 2025-10-12 18:45:53 +07:00
devlikepro 51aa0e3154 [core] ChatWoot - i18n - datetime 2025-10-12 18:45:52 +07:00
devlikepro 77893a0eb0 [core] ChatWoot - i18n - locale base 2025-10-12 18:45:52 +07:00
devlikepro 4a866d8a08 [core] ChatWoot - PIX
(cherry picked from bergpinheiro/waha@696de94859)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-10-12 18:45:52 +07:00
devlikepro f912353a5c [core] ChatWoot - Event Message
(cherry picked from bergpinheiro/waha@696de94859)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-10-12 18:45:51 +07:00
devlikepro a6a1d8dc28 [core] ChatWoot - Poll message
(cherry picked from bergpinheiro/waha@696de94859)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-10-12 18:45:50 +07:00
devlikepro 150e73aa74 [core] ChatWoot - adjust location message
(cherry picked from bergpinheiro/waha@696de94859)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-10-12 18:45:49 +07:00
devlikepro a9300b3134 [core] ChatWoot add URL test 2025-10-12 18:45:48 +07:00
devlikepro bbe3859085 [core] ChatWoot - Facebook Ads Message Template
closes #1461

(cherry picked from bergpinheiro/waha@5863590208)

Thanks @bergpinheiro
Co-authored-by: bergpinheiro <berg.pinheiro2009@gmail.com>
2025-10-12 18:45:48 +07:00
devlikepro b420548dd9 [core] export fetchBuffer 2025-10-12 18:45:47 +07:00
devlikepro 741483eec7 [core] ChatWoot - remove unused getAttachments in message.reaction 2025-10-12 18:45:47 +07:00
devlikepro 2acf7e4e17 [core] ChatWoot - add name for location message 2025-10-12 18:45:46 +07:00
devlikepro 9bca6d904d [core] ChatWoot - Refactor message.any 2025-10-12 18:45:45 +07:00
devlikepro 857c586df6 [core] AGENTS.md 2025-10-12 18:45:45 +07:00
devlikepro df8cb7cecb [core] NOWEB - fix ssl issue
fix #1474
2025-10-12 18:45:45 +07:00
devlikepro 9d0050adb8 [core] NOWEB - filter PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE
fix #1459
2025-10-12 18:45:44 +07:00
devlikepro df927ab04a [core] Remove + at the start
fix #1475
2025-10-12 18:45:43 +07:00
devlikepro 02e5934e8f [core] Up NOWEB 2025-10-10 - fixes 2025-10-12 18:45:42 +07:00
devlikepro 825a4152db [core] Up NOWEB 2025-10-10 2025-10-12 18:45:41 +07:00
devlikepro 81b10099af [core] Up WEBJS
fix #1469
fix #1468
2025-10-12 18:45:41 +07:00
devlikepro 3be5f328b0 [core] Up GOWS 2025-10-12 18:45:41 +07:00
devlikepro 5caa3b8b81 [core] do not rename title 2025-10-10 10:07:02 +07:00
devlikepro 8231bf7d8e [core] GitHub - en/pt by default 2025-10-06 18:30:15 +07:00
devlikepro 22d94808fb [core] Fix lint
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-04 17:12:34 +07:00
devlikepro 57daefa680 [core] 2025.9.8 2025-10-04 17:00:06 +07:00
devlikepro 5cf8704973 [core] NOWEB - no await in start
fix #1397
2025-10-04 17:00:05 +07:00
devlikepro 28da9c34bb [core] Fix NOWEB Apps on start
fix #1397
2025-10-04 17:00:05 +07:00
devlikepro 51ba5e3266 [core] Up GOWS
fix #1293
fix #1412
fix #1406
2025-10-04 17:00:05 +07:00
devlikepro f696794ee1 [core] Up WEBJS
fix #1276
fix #1407
2025-10-04 17:00:04 +07:00
devlikepro c6eca9510b [core] NOWEB - 2025-10-03 2025-10-04 17:00:04 +07:00
devlikepro 9a2fd2fccd [core] make up* 2025-10-04 17:00:04 +07:00
devlikepro c0a2d02f0d [core] NOWEB - send poll to newsletters
fix #1270
fix #1415
2025-10-04 17:00:04 +07:00
devlikepro 6493d16fd2 [core] NOWEB - try to get message just by id for poll
fix #1396
2025-10-04 17:00:04 +07:00
devlikeapro 9f96c678dc [core] support CSAT messages in webhook processing
fix(chatwoot): support CSAT messages in webhook processing
2025-10-04 16:59:36 +07:00
Berg Pinheiro a81f2ab553 fix(chatwoot): support CSAT messages in webhook processing
- Add support for 'input_csat' content_type in MessageHandler
- Previously CSAT messages were being ignored due to strict content_type check
- CSAT messages now processed as text messages and sent to WhatsApp
- Fixes issue where Chatwoot CSAT surveys were not being delivered to users

Changes:
- Modified content_type validation in message_created.ts to accept both 'text' and 'input_csat'
- CSAT messages will now be sent to WhatsApp with their survey content and links
- Maintains existing behavior for all other message types
2025-10-01 22:23:36 -03:00
77 changed files with 3736 additions and 323 deletions

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
name: 'github-translate'
on:
issues:
types: [opened]
issue_comment:
types: [created]
discussion:
types: [created]
discussion_comment:
types: [created]
# pull_request_target:
# types: [opened, edited]
# pull_request_review_comment:
# types: [created, edited]
jobs:
en:
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: true
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'
+1
View File
@@ -67,6 +67,7 @@ repos:
exclude: |
(?x)^(
entrypoint.sh|
AGENTS.md|
.pre-commit-config.yaml|
^src/core/engines/webjs/session.webjs.core.ts|
^src/core/engines/webjs/_lodash.js|
+124
View File
@@ -0,0 +1,124 @@
# WAHA Agent Playbook
This guide summarizes how to explore, modify, and validate the WhatsApp HTTP API
(WAHA) codebase when assisting as an automation or coding agent.
## Product & Variants
- WAHA ships in **Core** and **Plus** editions. Core lives under `src/core` and
only supports the default session plus minimal media features. Plus extends
core via `src/plus` to add multi-session orchestration, richer media handling,
and external storage integrations.
- Core code must remain free from Plus-only references. A pre-commit hook
rejects the word `plus` inside core files; reuse abstractions exposed through
`@waha/core/**` instead of importing Plus modules from Core.
- Commit subjects are validated: changes that touch `src/plus` require a
`[PLUS] …` prefix and must not include non-Plus files; everything else must
use `[core] …`. Verify against `./.precommit/validate_commit_message.py` if
unsure.
## Tech Stack Snapshot
- **Runtime**: Node.js 22.x, Yarn 3.6 (Berry). Always install packages and run
scripts with Yarn.
- **Framework**: NestJS v11 with dependency injection, modular controllers in
`src/api`, and Pino-based logging via `nestjs-pino`.
- **Engines**: WhatsApp engines are abstracted (`WEBJS`, `GOWS`, `NOWEB`). Core
uses `SessionManagerCore`; Plus swaps to `SessionManagerPlus` with extra
storage backends (Mongo/Postgres/SQLite).
- **ESM Bridge**: ESM-only dependencies (Baileys) load through
`src/vendor/esm.ts`. Add new ESM modules there to ensure they load exactly
once.
- **Utilities**: RxJS streams (`SwitchObservable`, `DefaultMap`) drive webhook
event fan-out. Prefer existing helpers in `src/utils` and `src/core/utils`
before adding bespoke logic.
## Repository Landmarks
- `src/main.ts`: runtime entry point; dynamically loads the correct AppModule
(Core vs Plus) and configures global interceptors/filters.
- `src/api/**`: REST controllers and WebSocket gateway. Keep handlers thin;
delegate to managers/services. HTTP routes follow `/api/{sessionName}/…`;
always thread the session name through request DTOs and guards instead of
hardcoding `default`.
- `src/core/**`: shared abstractions (config services, engine bootstrap,
storage, session management). Core only allows the `default` session and
cleans up storage on boot.
- `src/plus/**`: multi-session orchestration, advanced media services, and
external persistence layers (Mongo, Postgres). Reuse this layer when adding
Plus-only capabilities.
- `src/apps/**`: integrations (e.g., ChatWoot) and application-specific
services.
- `src/structures/**` and `src/utils/**`: DTOs, enums (event names follow
`domain.action`), helper utilities. Maintain naming consistency when
introducing new events.
- `tests/**`: Jest-based suites; do not add new tests unless explicitly asked,
but keep existing tests working.
## Coding Expectations
- Favor composable, long-lived solutions. If a helper already exists (e.g.,
`parseBool`, `DefaultMap`, media factories), extend it instead of reinventing
logic. Lodash ships with the project—prefer its utilities over hand-rolled
helpers. Import lodash as a namespace (`import * as lodash from 'lodash';`) so
helpers like `lodash.camelCase` stay consistent across files. When a new
third-party library seems necessary, confirm with the user, especially if the
package looks stale.
- Stick to NestJS patterns: inject dependencies through constructors, expose
provider tokens from modules, and keep controllers free from business logic.
- Logging goes through injected `PinoLogger` or helpers in
`src/utils/logging.ts`. `console.log` is blocked by pre-commit.
- Respect path aliases (`@waha/...`) defined in `tsconfig.json`; keep imports
consistent (use absolute aliases, not relative `../../../`).
- Avoid naming unused variables with a leading underscore; if a parameter is
required by a signature, explicitly `void` it instead.
- For configs, prefer runtime configurability over constants. Environment keys
follow `WAHA_*` for global values and `WAHA_SESSION_CONFIG_*` /
`session.config.*` for per-session overrides. If both env and config are
supported, honor both (`WAHA_WEBJS_CONFIG_*` vs. `session.webjs.config.*`).
## Language & Localization
- Keep identifiers (classes, methods, variables) and code comments in English so
the codebase stays consistent for the global team.
- Route user-visible strings through the existing i18n structure rather than
hardcoding text. ChatWoot copy belongs in `src/apps/chatwoot/i18n` with
English as the source locale before adding translations.
## ChatWoot Integration Notes
- Avoid introducing synthetic events; map onto existing webhook or engine events
whenever plausible. Always use the official ChatWoot API client located in
`src/apps/chatwoot/client`.
- When adding events, align consumer names with webhook/event identifiers (e.g.,
`message.any`).
## Workflow Checklist
1. Identify whether work targets Core, Plus, or shared layers. If Plus-only,
isolate changes to `src/plus` and ensure the commit prefix matches.
2. Lean on existing services/managers; extend the appropriate session manager
rather than branching logic inline.
3. After edits run:
- `pre-commit run --all-files`
- `yarn build`
- `yarn test` Use Node 22. Address lint or formatting issues before
proceeding.
4. Do **not** start the application yourself; ask the user to run it if runtime
validation is required.
5. Capture any assumptions or open questions for the user, especially when
touching configs, introducing dependencies, or modifying public APIs.
## Additional Tips
- Concurrency-sensitive sections (session start/stop) rely on `async-lock`. When
adding async flows, reuse `SessionManager.withLock` or existing retry
utilities (`promiseTimeout`, `waitUntil`).
- Media features centralize through `MediaManager` and `MediaStorageFactory`.
When altering media behavior, update both Core and Plus variants where
applicable.
- Keep docs and code ASCII unless a file already uses other characters. When
updating documentation, mirror the concise, actionable tone used here.
Following this playbook keeps contributions aligned with WAHA’s structure,
automation hooks, and release process.
+3 -3
View File
@@ -35,13 +35,13 @@ for-swagger:
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
up-noweb:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-09-21
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-10
up-noweb-libsignal:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
up-webjs:
. ${NVM_DIR}/nvm.sh && nvm exec 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-09-10
start-proxy:
docker run --rm -d --name squid-container -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta
+1 -1
View File
@@ -24,7 +24,7 @@ services:
- 8.8.8.8
base: &base
image: chatwoot/chatwoot:v4.5.2
image: chatwoot/chatwoot:v4.6.0
env_file: .chatwoot.env ## Change this file for customized env variables
volumes:
- chatwoot_storage:/app/storage
+5 -1
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-09-21",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-10",
"@adiwajshing/keyed-db": "^0.2.4",
"@aws-sdk/client-s3": "^3.633.0",
"@aws-sdk/s3-request-presigner": "^3.633.0",
@@ -79,6 +79,7 @@
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"long": "^5.3.2",
"mime-types": "^2.1.27",
"mongodb": "6.9.0",
"mustache": "^4.2.0",
@@ -148,6 +149,9 @@
"ts"
],
"rootDir": "src",
"moduleNameMapper": {
"^@waha/(.*)$": "<rootDir>/$1"
},
"testRegex": ".test.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
+12
View File
@@ -0,0 +1,12 @@
import { ILogger } from '@waha/apps/app_sdk/ILogger';
export function JestLogger(): ILogger {
return {
fatal: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
};
}
@@ -44,4 +44,8 @@ export class AppsDisabledService implements IAppsService {
async beforeSessionStart(session: WhatsappSession, store: DataStore) {
return;
}
async afterSessionStart(session: WhatsappSession, store: DataStore) {
return;
}
}
@@ -145,7 +145,17 @@ export class AppsEnabledService implements IAppsService {
const apps = await repo.getEnabledBySession(session.name);
for (const app of apps) {
const service = this.getAppService(app);
await service.beforeSessionStart(app, session);
service.beforeSessionStart(app, session);
}
}
async afterSessionStart(session: WhatsappSession, store: DataStore) {
const knex = store.getWAHADatabase();
const repo = new AppRepository(knex);
const apps = await repo.getEnabledBySession(session.name);
for (const app of apps) {
const service = this.getAppService(app);
service.afterSessionStart(app, session);
}
}
+3 -1
View File
@@ -23,5 +23,7 @@ export interface IAppService {
beforeDeleted(app: App): Promise<void>;
beforeSessionStart(app: App, session: WhatsappSession): Promise<void>;
beforeSessionStart(app: App, session: WhatsappSession): void;
afterSessionStart(app: App, session: WhatsappSession): void;
}
@@ -17,6 +17,8 @@ export interface IAppsService {
beforeSessionStart(session: WhatsappSession, store: DataStore): Promise<void>;
afterSessionStart(session: WhatsappSession, store: DataStore): Promise<void>;
migrate(knex: Knex): Promise<void>;
}
@@ -81,6 +81,19 @@ export class ChatwootWebhookController {
}
return { success: true };
case EventName.MESSAGE_UPDATED:
// We handle only "retries" on message_update
// This is the attribute ChatWoot send
// There's NO other way to identify "status: read" updates right now
// There's no "body.status" in message_updated webhook :(
const isRetryNull = body.content_attributes?.external_error === null;
const isRetrySomething = Boolean(
body.content_attributes?.external_error,
);
const isRetry = isRetryNull || isRetrySomething;
if (!isRetry) {
return { success: true };
}
if (!isCommandsChat) {
await this.chatWootQueueService.addMessageUpdatedJob(data);
} else {
+4 -3
View File
@@ -1,7 +1,8 @@
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
import * as NodeCache from 'node-cache';
import { ConversationId, IConversationCache } from './IConversationCache';
import { IConversationCache } from './IConversationCache';
import { ContactIds } from '@waha/apps/chatwoot/client/ConversationService';
const cache: NodeCache = new NodeCache({
stdTTL: 24 * 60 * 60, // 1 day
@@ -24,7 +25,7 @@ class ConversationCache implements IConversationCache {
cache.del(fullKey);
}
get(key: string): ConversationId | null {
get(key: string): ContactIds | null {
const fullKey = this.fullKey(key);
return cache.get(fullKey) || null;
}
@@ -34,7 +35,7 @@ class ConversationCache implements IConversationCache {
return cache.has(fullKey);
}
set(key: string, value: ConversationId): void {
set(key: string, value: ContactIds): void {
const fullKey = this.fullKey(key);
cache.set(fullKey, value);
}
+3 -3
View File
@@ -1,8 +1,8 @@
export type ConversationId = number;
import { ContactIds } from '@waha/apps/chatwoot/client/ConversationService';
export interface IConversationCache {
get(key: string): ConversationId | null;
set(key: string, value: ConversationId): void;
get(key: string): ContactIds | null;
set(key: string, value: ContactIds): void;
has(key: string): boolean;
delete(key: string): void;
clean(): void;
+6
View File
@@ -21,6 +21,7 @@ import { WAHAMessageAnyConsumer } from './consumers/waha/message.any';
import { WAHAMessageEditedConsumer } from './consumers/waha/message.edited';
import { WAHAMessageReactionConsumer } from './consumers/waha/message.reaction';
import { WAHAMessageRevokedConsumer } from './consumers/waha/message.revoked';
import { WAHAMessageAckConsumer } from './consumers/waha/message.ack';
import { WAHASessionStatusConsumer } from './consumers/waha/session.status';
import { ChatWootQueueService } from './services/ChatWootQueueService';
import { ChatWootScheduleService } from './services/ChatWootScheduleService';
@@ -55,6 +56,10 @@ const IMPORTS = lodash.flatten([
name: QueueName.WAHA_MESSAGE_REVOKED,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
}),
RegisterAppQueue({
name: QueueName.WAHA_MESSAGE_ACK,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
}),
RegisterAppQueue({
name: QueueName.WAHA_SESSION_STATUS,
defaultJobOptions: merge(ExponentialRetriesJobOptions, JobRemoveOptions),
@@ -98,6 +103,7 @@ const PROVIDERS = [
WAHAMessageReactionConsumer,
WAHAMessageEditedConsumer,
WAHAMessageRevokedConsumer,
WAHAMessageAckConsumer,
MessageCleanupConsumer,
CheckVersionConsumer,
ChatWootWAHAQueueService,
@@ -0,0 +1,26 @@
import axios from 'axios';
export class ChatWootInboxNewAPI {
constructor(
private base: string,
private inboxIdentifier: string,
) {}
async updateLastSeen(sourceId: string, conversationId: number) {
// TODO: Move to the lib (fork?)
// Call the update_last_seen endpoint using a direct HTTP request
// since the SDK doesn't have this method
const path = `/public/api/v1/inboxes/${this.inboxIdentifier}/contacts/${sourceId}/conversations/${conversationId}/update_last_seen`;
const url = new URL(path, this.base).toString();
await axios.post(
url,
{},
{
headers: {
api_access_token: this.inboxIdentifier,
'Content-Type': 'application/json',
},
},
);
}
}
@@ -5,16 +5,16 @@ import ChatwootClient, {
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { ContactService } from '@waha/apps/chatwoot/client/ContactService';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { ConversationService } from '@waha/apps/chatwoot/client/ConversationService';
import {
ContactIds,
ConversationService,
} from '@waha/apps/chatwoot/client/ConversationService';
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
import { InboxContactInfo } from '@waha/apps/chatwoot/contacts/InboxContactInfo';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { CacheForConfig } from '../cache/ConversationCache';
import {
ConversationId,
IConversationCache,
} from '../cache/IConversationCache';
import { IConversationCache } from '../cache/IConversationCache';
export interface ContactInfo {
ChatId(): string;
@@ -42,7 +42,7 @@ export class ContactConversationService {
private async upsertByContactInfo(
contactInfo: ContactInfo,
): Promise<ConversationId> {
): Promise<ContactIds> {
const chatId = contactInfo.ChatId();
// Check cache for chat id
@@ -96,19 +96,24 @@ export class ContactConversationService {
);
// Save to cache
this.cache.set(chatId, conversation.id);
return conversation.id;
const ids = {
id: conversation.id,
sourceId: contact.sourceId,
};
this.cache.set(chatId, ids);
return ids;
}
public async ConversationByContact(
contactInfo: ContactInfo,
): Promise<Conversation> {
const chatId = contactInfo.ChatId();
const conversationId = await this.upsertByContactInfo(contactInfo);
const ids = await this.upsertByContactInfo(contactInfo);
const conversation = new Conversation(
this.accountAPI,
this.config.accountId,
conversationId,
ids.id,
ids.sourceId,
);
conversation.onError = (err) => {
if (err instanceof ChatWootAPIError) {
@@ -116,7 +121,7 @@ export class ContactConversationService {
this.cache.delete(chatId);
this.logger.error(`ApiError: ${err.message}`);
this.logger.error(
`ApiError occurred, invalidating cache for chat.id: ${chatId}, conversation.id: ${conversationId}`,
`ApiError occurred, invalidating cache for chat.id: ${chatId}, conversation.id: ${ids.id}, source.id: ${ids.sourceId}`,
);
}
};
@@ -146,18 +151,25 @@ export class ContactConversationService {
}
}
public ResetMismatchedCache(chatIds: Array<string>, value: ConversationId) {
public ResetMismatchedCache(chatIds: Array<string>, contactId: number) {
for (const chatId of chatIds) {
if (!this.cache.has(chatId)) {
continue;
}
const current = this.cache.get(chatId);
if (current !== value) {
if (current.id !== contactId) {
this.logger.info(
`Resetting cache for chat id: ${chatId}, value changed from ${current} to ${value}`,
`Resetting cache for chat id: ${chatId}, value changed from ${current} to ${contactId}`,
);
this.cache.delete(chatId);
}
}
}
public async markConversationAsRead(
conversationId: number,
sourceId: string,
): Promise<void> {
await this.conversationService.markAsRead(conversationId, sourceId);
}
}
+1
View File
@@ -9,6 +9,7 @@ export class Conversation {
private accountAPI: ChatwootClient,
private accountId: number,
public conversationId: number,
public sourceId: string | null = null,
) {}
public async send(data: conversation_message_create) {
@@ -6,6 +6,8 @@ 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'>;
@@ -54,4 +56,23 @@ export class ConversationService {
);
return conversation;
}
async markAsRead(conversationId: number, sourceId: string): Promise<void> {
try {
const inboxNewAPI = new ChatWootInboxNewAPI(
this.config.url,
this.config.inboxIdentifier,
);
await inboxNewAPI.updateLastSeen(sourceId, conversationId);
this.logger.info(
`Marked conversation.id: ${conversationId} as read in inbox: ${this.config.inboxIdentifier} for contact: ${sourceId}`,
);
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
this.logger.error(
`Error marking conversation.id: ${conversationId} as read: ${reason}`,
);
throw error;
}
}
}
+1
View File
@@ -13,6 +13,7 @@ export enum QueueName {
WAHA_MESSAGE_REACTION = 'chatwoot.waha | message.reaction',
WAHA_MESSAGE_EDITED = 'chatwoot.waha | message.edited',
WAHA_MESSAGE_REVOKED = 'chatwoot.waha | message.revoked',
WAHA_MESSAGE_ACK = 'chatwoot.waha | message.ack',
//
// ChatWoot Events - Real
//
@@ -15,7 +15,7 @@ import {
MessageMapping,
MessageMappingService,
} from '@waha/apps/chatwoot/storage';
import { MarkdownToWhatsApp } from '@waha/apps/chatwoot/text';
import { MarkdownToWhatsApp } from '@waha/apps/chatwoot/messages/to/whatsapp/markdown';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import {
@@ -80,7 +80,10 @@ export class MessageHandler {
async handle(body: any) {
const chatId = await LookupAndCheckChatId(this.session, body);
const message = body;
if (message.content_type != 'text') {
if (
message.content_type != 'text' &&
message.content_type != 'input_csat'
) {
this.logger.info(
`Message content type not supported. Content type: ${message.content_type}`,
);
+9 -1
View File
@@ -42,6 +42,7 @@ export function ListenEventsForChatWoot() {
WAHAEvents.MESSAGE_REACTION,
WAHAEvents.MESSAGE_EDITED,
WAHAEvents.MESSAGE_REVOKED,
WAHAEvents.MESSAGE_ACK,
WAHAEvents.SESSION_STATUS,
];
}
@@ -96,8 +97,15 @@ export abstract class ChatWootWAHABaseConsumer extends AppConsumer {
messageInfo: IMessageInfo,
): Promise<any>;
ShouldProcess(event: any): boolean {
return true;
}
async processJob(job: Job<EventData, any, WAHAEvents>): Promise<any> {
const event: WAHAWebhookMessageRevoked = job.data.event as any;
const event = job.data.event as any;
if (!this.ShouldProcess(event)) {
return;
}
const key = WhatsAppChatIdKey(job.data.app, this.GetChatId(event));
return await this.withMutex(job, key, () =>
this.ProcessAndReportErrors(job),
@@ -0,0 +1,123 @@
import { Processor } from '@nestjs/bullmq';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { ContactConversationService } from '@waha/apps/chatwoot/client/ContactConversationService';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import {
ChatWootWAHABaseConsumer,
IMessageInfo,
} 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 { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents, WAMessageAck } 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';
@Processor(QueueName.WAHA_MESSAGE_ACK, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
constructor(
protected readonly manager: SessionManager,
log: PinoLogger,
rmutex: RMutexService,
) {
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;
}
const payload = event.payload;
if (payload.fromMe) {
// Ignore from me
return;
}
// Only READ and PLAYED
return (
payload.ack === WAMessageAck.READ || payload.ack == WAMessageAck.PLAYED
);
}
GetChatId(event: WAHAWebhookMessageAck): string {
return event.payload.from;
}
async Process(
job: Job<EventData, any, WAHAEvents>,
info: IMessageInfo,
): Promise<void> {
const container = await this.DIContainer(job, job.data.app);
const event = job.data.event as WAHAWebhookMessageAck;
const session = new WAHASessionAPI(event.session, container.WAHASelf());
const handler = new MessageAckHandler(
container.ContactConversationService(),
container.Logger(),
info,
session,
container.Locale(),
);
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,
private readonly logger: ILogger,
private readonly info: IMessageInfo,
private readonly session: WAHASessionAPI,
private readonly locale: Locale,
) {}
async handle(event: WAHAWebhookMessageAck): Promise<void> {
const payload = event.payload;
const contactInfo = WhatsAppContactInfo(
this.session,
payload.from,
this.locale,
);
const conversation =
await this.contactConversationService.ConversationByContact(contactInfo);
this.info.onConversationId(conversation.conversationId);
const sourceId = conversation.sourceId;
if (!sourceId) {
this.logger.warn(
`Contact not found for chat.id: ${payload.from}. Skipping read update for message ${payload.id}`,
);
return;
}
try {
await this.contactConversationService.markConversationAsRead(
conversation.conversationId,
sourceId,
);
this.logger.info(
`Marked conversation ${conversation.conversationId} as read for chat.id: ${payload.from} (message: ${payload.id}, sourceId: ${sourceId})`,
);
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
this.logger.error(
`Error marking conversation ${conversation.conversationId} as read for chat.id: ${payload.from}. Reason: ${reason}`,
);
throw error;
}
}
}
+48 -154
View File
@@ -1,7 +1,5 @@
import type { proto } from '@adiwajshing/baileys';
import { Processor } from '@nestjs/bullmq';
import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import {
@@ -11,7 +9,6 @@ import {
MessageBaseHandler,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/text';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -20,13 +17,18 @@ import { WAMessage } from '@waha/structures/responses.dto';
import { WAHAWebhookMessageAny } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
import * as lodash from 'lodash';
import { parseVCardV3, SimpleVCardInfo } from '@waha/core/vcard';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mime = require('mime-types');
import {
FacebookAdMessage,
EventMessage,
PixMessage,
LocationMessage,
MessageToChatWootConverter,
PollMessage,
ShareContactMessage,
TextMessage,
UnsupportedMessage,
resolveProtoMessage,
} from '@waha/apps/chatwoot/messages/to/chatwoot';
@Processor(QueueName.WAHA_MESSAGE_ANY, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
@@ -63,138 +65,60 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
}
}
function isEmptyString(content: string) {
if (!content) {
return true;
}
return content == '' || content == '\n';
}
class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
protected async getMessage(
payload: WAMessage,
): Promise<ChatWootMessagePartial> {
const protoMessage = this.getProtoMessage(payload);
let msg = await this.getTextMessage(payload, protoMessage);
let converter: MessageToChatWootConverter;
let msg: ChatWootMessagePartial;
const protoMessage = resolveProtoMessage(payload);
// Check for Facebook Ad first - but let it use the normal flow later
converter = new FacebookAdMessage(this.l, this.logger);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
msg = this.getLocationMessage(payload, protoMessage);
converter = new TextMessage(this.l, this.logger, this.waha);
msg = await converter.convert(payload, null);
if (msg) {
return msg;
}
msg = this.getShareContactMessage(payload, protoMessage);
converter = new LocationMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
return this.getUnsupportedMessage();
}
protected async getTextMessage(
payload: WAMessage,
_: proto.Message | null,
): Promise<ChatWootMessagePartial | null> {
const attachments = await this.getAttachments(payload);
let content = this.l
.key(TKey.WA_TO_CW_MESSAGE)
.render({ payload: payload });
if (isEmptyString(content) && attachments.length == 0) {
return null;
}
if (isEmptyString(content)) {
content = null;
}
return {
content: WhatsappToMarkdown(content),
attachments: attachments,
private: undefined,
};
}
private getLocationMessage(
payload: WAMessage,
message: proto.Message | null,
): ChatWootMessagePartial | null {
const hasLocation = !lodash.isEmpty(message?.locationMessage);
const hasLiveLocation = !lodash.isEmpty(message?.liveLocationMessage);
if (!hasLocation && !hasLiveLocation) {
return null;
}
const location = this.l.key(TKey.WA_TO_CW_MESSAGE_LOCATION).r({
payload,
message,
});
if (isEmptyString(location)) {
return null;
}
return {
content: location,
attachments: [],
private: undefined,
};
}
private getShareContactMessage(
_payload: WAMessage,
message: proto.Message | null,
): ChatWootMessagePartial | null {
let vcards: string[] = [];
if (!lodash.isEmpty(message?.contactsArrayMessage?.contacts)) {
vcards = message!.contactsArrayMessage!.contacts.map((c) => c.vcard);
} else if (!lodash.isEmpty(message?.contactMessage?.vcard)) {
vcards = [message!.contactMessage!.vcard];
}
if (vcards.length === 0) {
return null;
}
const attachments: SendAttachment[] = vcards.map((v, i) => ({
content: Buffer.from(v, 'utf8').toString('base64'),
encoding: 'base64',
filename: `vcard-${i + 1}.vcf`,
}));
let contacts: SimpleVCardInfo[] = [];
try {
contacts = vcards.map(parseVCardV3);
} catch (err) {
this.logger.error(
`Error parsing some vcards: vcards=${vcards}, err=${err}`,
);
}
const msg = this.l.key(TKey.WA_TO_CW_MESSAGE_CONTACTS).r({ contacts });
if (contacts.length === 0 && attachments.length === 0) {
return null;
converter = new ShareContactMessage(this.l, this.logger);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
return {
content: msg,
attachments: attachments,
private: undefined,
};
}
private getUnsupportedMessage(): ChatWootMessagePartial {
const unsupported = this.l
.key(TKey.WA_TO_CW_MESSAGE_UNSUPPORTED)
.render({ details: JobLink(this.job) });
return {
content: unsupported,
attachments: [],
private: true,
};
}
private getProtoMessage(payload: WAMessage): proto.Message | null {
// GOWS
if (payload._data.Message) {
return payload._data.Message;
converter = new PollMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
// NOWEB
if (payload._data.message) {
return payload._data.message;
converter = new EventMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
// WEBJS - not available
return null;
converter = new PixMessage(this.l, this.logger);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
converter = new UnsupportedMessage(this.l, this.job);
msg = await converter.convert(payload, protoMessage);
return msg;
}
getReplyToWhatsAppID(payload: WAMessage): string {
@@ -205,34 +129,4 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
const key = parseMessageIdSerialized(replyTo.id, true);
return key.id;
}
async getAttachments(payload: WAMessage): Promise<SendAttachment[]> {
//
// WAHA
//
const hasMedia = payload.media?.url;
if (!hasMedia) {
return [];
}
const attachments: SendAttachment[] = [];
const media = payload.media;
this.logger.info(`Downloading media from '${media.url}'...`);
const buffer = await this.waha.fetch(media.url);
const fileContent = buffer.toString('base64');
let filename = media.filename;
if (!filename) {
const extension = mime.extension(media.mimetype);
filename = `no-filename.${extension}`;
}
const attachment: SendAttachment = {
content: fileContent,
filename: filename,
encoding: 'base64',
};
attachments.push(attachment);
this.logger.info(`Downloaded media from '${media.url}' as '${filename}'`);
return attachments;
}
}
@@ -6,9 +6,8 @@ import {
ChatWootMessagePartial,
ChatWootWAHABaseConsumer,
IMessageInfo,
MessageBaseHandler,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { MessageBaseHandler } from '@waha/apps/chatwoot/consumers/waha/base';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/text';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents } from '@waha/structures/enums.dto';
@@ -19,10 +18,12 @@ import {
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { SendAttachment } from '../../client/types';
import { WAHASessionAPI } from '../../session/WAHASelf';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { WAMessage } from '@waha/structures/responses.dto';
import {
MessageEdited,
MessageToChatWootConverter,
resolveProtoMessage,
} from '@waha/apps/chatwoot/messages/to/chatwoot';
@Processor(QueueName.WAHA_MESSAGE_EDITED, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
@@ -63,25 +64,12 @@ class MessageEditedHandler extends MessageBaseHandler<WAMessageEditedBody> {
protected async getMessage(
payload: WAMessageEditedBody,
): Promise<ChatWootMessagePartial> {
const body = payload.body;
const formatted = WhatsappToMarkdown(body);
const content = this.l
.key(TKey.MESSAGE_EDITED_IN_WHATSAPP)
.render({ text: formatted });
return {
content: content,
attachments: [],
private: undefined,
};
const protoMessage = resolveProtoMessage(payload);
const converter: MessageToChatWootConverter = new MessageEdited(this.l);
return converter.convert(payload, protoMessage);
}
getReplyToWhatsAppID(payload: WAMessageEditedBody): string {
return payload.editedMessageId;
}
async getAttachments(
payload: WAMessageEditedBody,
): Promise<SendAttachment[]> {
return [];
}
}
@@ -82,8 +82,4 @@ export class MessageReactionHandler extends MessageBaseHandler<WAMessageReaction
const key = parseMessageIdSerialized(messageId, false);
return key.id;
}
async getAttachments(payload: WAMessageReaction): Promise<SendAttachment[]> {
return [];
}
}
@@ -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,
+62 -1
View File
@@ -1,9 +1,19 @@
import * as Mustache from 'mustache';
import { TemplatePayloads, TKey } from '@waha/apps/chatwoot/i18n/templates';
import Long from 'long';
import { ensureNumber } from '@waha/core/engines/noweb/utils';
export class Locale {
constructor(private readonly strings: Record<string, string>) {}
/**
* Return Base Locale
* For number, datetime, currency formats
*/
get locale(): string {
return this.strings['locale.base'] || 'en';
}
key<K extends TKey>(key: K): Template<K> {
return new Template(this.strings[key] || key);
}
@@ -15,13 +25,64 @@ export class Locale {
override(strings: Record<string, string>): Locale {
return new Locale({ ...this.strings, ...strings });
}
FormatCurrency(
currency: string,
value: number | null,
offset: number = 1,
): string | null {
if (value == null) {
return null;
}
if (!currency) {
return null;
}
offset = offset || 1;
try {
const fmt = new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
});
return fmt.format(value / offset);
} catch (err) {
return `${currency} ${value}`;
}
}
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);
}
FormatTimestamp(timestamp: Long | string | number | null): string | null {
const value = ensureNumber(timestamp);
if (!value) {
return undefined;
}
if (!Number.isFinite(value)) {
return undefined;
}
const milliseconds = value >= 1e12 ? value : value * 1000;
const date = new Date(milliseconds);
if (Number.isNaN(date.getTime())) {
return undefined;
}
return this.FormatDatetime(date);
}
}
export class Template<K extends TKey> {
constructor(private readonly template: string) {}
render(data: TemplatePayloads[K]): string {
return Mustache.render(this.template, data);
const text = Mustache.render(this.template, data);
// Remove /n at the end if any
return text.replace(/\n$/, '');
}
r(data: TemplatePayloads[K]): string {
+120 -2
View File
@@ -1,8 +1,28 @@
# Arabic translations for ChatWoot integration
locale.name: '🇦🇪 العربية (Arabic)'
# رمز اللغة الأساسي - يجب أن يكون محليًا صالحًا وفق BCP 47
# https://en.wikipedia.org/wiki/IETF_language_tag
# تستخدمه WAHA لعرض الأرقام والعملات والتاريخ/الوقت
locale.base: 'ar-AE'
app.inbox.contact.name: 'تكامل واتساب (WAHA)'
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:
locales: ar-AE
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Dubai'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **الموقع المشترك**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **العنوان:** {{address}}
{{/address}}
{{#url}}
🌐 **الموقع الإلكتروني:** {{url}}
{{/url}}
🔗 **عرض على الخريطة:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **مشاركة الموقع المباشر**
افتح **WhatsApp** لعرض التحديثات اللحظية.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **العنوان:** {{address}}
{{/address}}
{{#url}}
🌐 **الموقع الإلكتروني:** {{url}}
{{/url}}
🔗 **عرض على الخريطة:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **استطلاع**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **حدث**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **الموقع:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **البدء:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **الانتهاء:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **رابط الانضمام:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - نسخ ولصق**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**مفتاح PIX:** {{key}}
{{/key}}
{{#keyType}}
**النوع:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**المبلغ:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**المرجع:** {{referenceId}}
{{/referenceId}}
💳 **تم إرسال PIX نسخ ولصق**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **هذا النوع من الرسائل غير مدعوم في ChatWoot.**
📱 يرجى فتح **WhatsApp** لعرضه.
التفاصيل: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **إعلان فيسبوك**
**العنوان:** {{{adData.title}}}
**الوصف:**
{{{adData.body}}}
**رابط الإعلان:** [عرض على فيسبوك]({{{adData.sourceUrl}}})
**معرّف الإعلان:** {{{adData.sourceId}}}
---
💬 **رسالة العميل:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Bengali translations for ChatWoot integration
locale.name: '🇧🇩 বাংলা (Bengali)'
# বেস লোকেল কোড - এটি একটি বৈধ BCP 47 লোকেল হতে হবে
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA এটি সংখ্যা, মুদ্রা এবং তারিখ/সময়ের ফরম্যাটিংয়ে ব্যবহার করে।
locale.base: 'bn-BD'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন (WAHA)'
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:
locales: bn-BD
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Dhaka'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **শেয়ার করা লোকেশন**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **ঠিকানা:** {{address}}
{{/address}}
{{#url}}
🌐 **ওয়েবসাইট:** {{url}}
{{/url}}
🔗 **মানচিত্রে দেখুন:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **লাইভ লোকেশন শেয়ার করা হচ্ছে**
রিয়েল-টাইম আপডেট দেখতে **WhatsApp** খুলুন।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **ঠিকানা:** {{address}}
{{/address}}
{{#url}}
🌐 **ওয়েবসাইট:** {{url}}
{{/url}}
🔗 **মানচিত্রে দেখুন:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **জরিপ**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **ইভেন্ট**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **স্থান:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **শুরুর সময়:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **শেষের সময়:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **যোগদানের লিঙ্ক:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - কপি এবং পেস্ট**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX কী:** {{key}}
{{/key}}
{{#keyType}}
**ধরন:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**পরিমাণ:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**রেফারেন্স:** {{referenceId}}
{{/referenceId}}
💳 **PIX কপি এবং পেস্ট পাঠানো হয়েছে**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **এই ধরনের বার্তা ChatWoot-এ সমর্থিত নয়।**
📱 এটি দেখতে অনুগ্রহ করে **WhatsApp** খুলুন।
বিস্তারিত: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ফেসবুক বিজ্ঞাপন**
**শিরোনাম:** {{{adData.title}}}
**বর্ণনা:**
{{{adData.body}}}
**বিজ্ঞাপনের লিংক:** [ফেসবুকে দেখুন]({{{adData.sourceUrl}}})
**বিজ্ঞাপনের আইডি:** {{{adData.sourceId}}}
---
💬 **গ্রাহকের বার্তা:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# German translations for ChatWoot integration
locale.name: '🇩🇪 Deutsch (German)'
# Basis-Gebietsschema-Code - muss eine gültige BCP 47 Locale sein
# https://en.wikipedia.org/wiki/IETF_language_tag
# 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.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:
locales: de-DE
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Berlin'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -219,7 +239,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Geteilter Standort**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adresse:** {{address}}
{{/address}}
{{#url}}
🌐 **Webseite:** {{url}}
{{/url}}
🔗 **Auf Karte ansehen:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -227,13 +259,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Live-Standort wird geteilt**
Öffne **WhatsApp**, um Aktualisierungen in Echtzeit zu sehen.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adresse:** {{address}}
{{/address}}
{{#url}}
🌐 **Webseite:** {{url}}
{{/url}}
🔗 **Auf Karte ansehen:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Umfrage**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Veranstaltung**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Ort:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Beginn:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Ende:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Teilnahmelink:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Kopieren und Einfügen**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX-Schlüssel:** {{key}}
{{/key}}
{{#keyType}}
**Typ:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Betrag:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Referenz:** {{referenceId}}
{{/referenceId}}
💳 **PIX Kopieren und Einfügen gesendet**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Dieser Nachrichtentyp wird in ChatWoot nicht unterstützt.**
📱 Bitte öffne **WhatsApp**, um ihn anzusehen.
Details: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK-ANZEIGE**
**Titel:** {{{adData.title}}}
**Beschreibung:**
{{{adData.body}}}
**Anzeigenlink:** [Auf Facebook ansehen]({{{adData.sourceUrl}}})
**Anzeigen-ID:** {{{adData.sourceId}}}
---
💬 **Kundennachricht:**
{{{payload.body}}}
+118 -2
View File
@@ -1,9 +1,27 @@
# English translations for ChatWoot integration
locale.name: '🇺🇸 English (English)'
# Base locale code - must be a valid BCP 47 locale
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA uses it for number, currency and datetime representation
locale.base: 'en-US'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
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:
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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/New_York'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -95,7 +113,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Shared Location**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Address:** {{address}}
{{/address}}
{{#url}}
🌐 **Website:** {{url}}
{{/url}}
🔗 **View on map:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -103,17 +133,103 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Sharing Live Location**
Open **WhatsApp** to see real-time updates.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Address:** {{address}}
{{/address}}
{{#url}}
🌐 **Website:** {{url}}
{{/url}}
🔗 **View on map:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Poll**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Event**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Location:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Start:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **End:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Join Link:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Copy and Paste**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX Key:** {{key}}
{{/key}}
{{#keyType}}
**Type:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Amount:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Reference:** {{referenceId}}
{{/referenceId}}
💳 **PIX Copy and Paste sent**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **This message type is not supported in ChatWoot.**
📱 Please open **WhatsApp** to view it.
Details: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK AD**
**Title:** {{{adData.title}}}
**Description:**
{{{adData.body}}}
**Ad Link:** [View on Facebook]({{{adData.sourceUrl}}})
**Ad ID:** {{{adData.sourceId}}}
---
💬 **CUSTOMER MESSAGE:**
{{{payload.body}}}
app.connected.message: |-
🔗 **CONNECTED: WhatsApp Session to ChatWoot Inbox**
+120 -2
View File
@@ -1,8 +1,28 @@
# Spanish translations for ChatWoot integration
locale.name: '🇪🇸 Español (Spanish)'
# Código de locale base - debe ser un locale BCP 47 válido
# https://en.wikipedia.org/wiki/IETF_language_tag
# 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.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:
locales: es-ES
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Madrid'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -219,7 +239,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Ubicación compartida**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Dirección:** {{address}}
{{/address}}
{{#url}}
🌐 **Sitio web:** {{url}}
{{/url}}
🔗 **Ver en el mapa:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -227,13 +259,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Compartiendo ubicación en tiempo real**
Abre **WhatsApp** para ver las actualizaciones en tiempo real.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Dirección:** {{address}}
{{/address}}
{{#url}}
🌐 **Sitio web:** {{url}}
{{/url}}
🔗 **Ver en el mapa:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Encuesta**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Evento**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Ubicación:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Inicio:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Fin:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Enlace para unirse:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Copiar y Pegar**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Clave PIX:** {{key}}
{{/key}}
{{#keyType}}
**Tipo:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Importe:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Referencia:** {{referenceId}}
{{/referenceId}}
💳 **PIX Copiar y Pegar enviado**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Este tipo de mensaje no es compatible con ChatWoot.**
📱 Por favor, abre **WhatsApp** para verlo.
Detalles: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ANUNCIO DE FACEBOOK**
**Título:** {{{adData.title}}}
**Descripción:**
{{{adData.body}}}
**Enlace del anuncio:** [Ver en Facebook]({{{adData.sourceUrl}}})
**ID del anuncio:** {{{adData.sourceId}}}
---
💬 **Mensaje del cliente:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Persian translations for ChatWoot integration
locale.name: '🇮🇷 فارسی (Persian)'
# کد محلی پایه - باید یک محلی معتبر BCP 47 باشد
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA از آن برای نمایش اعداد، ارز و تاریخ/زمان استفاده می‌کند
locale.base: 'fa-IR'
app.inbox.contact.name: 'ادغام واتساپ (WAHA)'
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:
locales: fa-IR
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Tehran'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **موقعیت مکانی به‌اشتراک‌گذاشته‌شده**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **آدرس:** {{address}}
{{/address}}
{{#url}}
🌐 **وب‌سایت:** {{url}}
{{/url}}
🔗 **مشاهده روی نقشه:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **اشتراک‌گذاری موقعیت مکانی زنده**
برای دیدن به‌روزرسانی‌های لحظه‌ای **WhatsApp** را باز کنید.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **آدرس:** {{address}}
{{/address}}
{{#url}}
🌐 **وب‌سایت:** {{url}}
{{/url}}
🔗 **مشاهده روی نقشه:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **نظرسنجی**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **رویداد**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **مکان:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **شروع:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **پایان:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **لینک پیوستن:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - کپی و چسباندن**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**کلید PIX:** {{key}}
{{/key}}
{{#keyType}}
**نوع:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**مبلغ:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**شناسه مرجع:** {{referenceId}}
{{/referenceId}}
💳 **PIX کپی و چسباندن ارسال شد**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **این نوع پیام در ChatWoot پشتیبانی نمی‌شود.**
📱 لطفاً برای مشاهده آن **WhatsApp** را باز کنید.
جزئیات: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **آگهی فیس‌بوک**
**عنوان:** {{{adData.title}}}
**توضیحات:**
{{{adData.body}}}
**لینک آگهی:** [مشاهده در فیس‌بوک]({{{adData.sourceUrl}}})
**شناسه آگهی:** {{{adData.sourceId}}}
---
💬 **پیام مشتری:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# French translations for ChatWoot integration
locale.name: '🇫🇷 Français (French)'
# Code de locale de base - doit être une locale BCP 47 valide
# https://en.wikipedia.org/wiki/IETF_language_tag
# 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.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:
locales: fr-FR
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Paris'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Position partagée**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adresse :** {{address}}
{{/address}}
{{#url}}
🌐 **Site web :** {{url}}
{{/url}}
🔗 **Voir sur la carte :** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Partage de position en direct**
Ouvrez **WhatsApp** pour voir les mises à jour en temps réel.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adresse :** {{address}}
{{/address}}
{{#url}}
🌐 **Site web :** {{url}}
{{/url}}
🔗 **Voir sur la carte :** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Sondage**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Événement**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Lieu :** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Début :** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Fin :** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Lien de participation :** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Copier et Coller**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Clé PIX :** {{key}}
{{/key}}
{{#keyType}}
**Type :** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Montant :** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Référence :** {{referenceId}}
{{/referenceId}}
💳 **PIX Copier et Coller envoyé**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Ce type de message n’est pas pris en charge dans ChatWoot.**
📱 Veuillez ouvrir **WhatsApp** pour l’afficher.
Détails: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **PUBLICITÉ FACEBOOK**
**Titre:** {{{adData.title}}}
**Description:**
{{{adData.body}}}
**Lien de l'annonce :** [Voir sur Facebook]({{{adData.sourceUrl}}})
**ID de l'annonce :** {{{adData.sourceId}}}
---
💬 **Message du client :**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Hebrew translations for ChatWoot integration
locale.name: '🇮🇱 עברית (Hebrew)'
# קוד הלוקאל הבסיסי - חייב להיות לוקאל תקין לפי BCP 47
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA משתמש בכך להצגת מספרים, מטבע ותאריך/שעה
locale.base: 'he-IL'
app.inbox.contact.name: 'אינטגרציית וואטסאפ (WAHA)'
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:
locales: he-IL
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Jerusalem'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **מיקום משותף**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **כתובת:** {{address}}
{{/address}}
{{#url}}
🌐 **אתר אינטרנט:** {{url}}
{{/url}}
🔗 **הצג במפה:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **משתף מיקום חי**
פתח/י את **WhatsApp** כדי לראות עדכונים בזמן אמת.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **כתובת:** {{address}}
{{/address}}
{{#url}}
🌐 **אתר אינטרנט:** {{url}}
{{/url}}
🔗 **הצג במפה:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **סקר**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **אירוע**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **מיקום:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **התחלה:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **סיום:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **קישור הצטרפות:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - העתק והדבק**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**מפתח PIX:** {{key}}
{{/key}}
{{#keyType}}
**סוג:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**סכום:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**אסמכתא:** {{referenceId}}
{{/referenceId}}
💳 **PIX העתק והדבק נשלח**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **סוג הודעה זה אינו נתמך ב-ChatWoot.**
📱 אנא פתח/י את **WhatsApp** כדי לצפות בזה.
פרטים: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **מודעת פייסבוק**
**כותרת:** {{{adData.title}}}
**תיאור:**
{{{adData.body}}}
**קישור למודעה:** [לצפייה בפייסבוק]({{{adData.sourceUrl}}})
**מזהה המודעה:** {{{adData.sourceId}}}
---
💬 **הודעת הלקוח:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Hindi translations for ChatWoot integration
locale.name: '🇮🇳 हिंदी (Hindi)'
# बेस लोकैल कोड - यह एक मान्य BCP 47 लोकैल होना चाहिए
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA इसे संख्याएँ, मुद्रा और दिनांक/समय प्रदर्शित करने के लिए उपयोग करता है।
locale.base: 'hi-IN'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन (WAHA)'
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:
locales: hi-IN
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Kolkata'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **साझा स्थान**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **पता:** {{address}}
{{/address}}
{{#url}}
🌐 **वेबसाइट:** {{url}}
{{/url}}
🔗 **मानचित्र पर देखें:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **लाइव लोकेशन साझा की जा रही है**
रीयल-टाइम अपडेट देखने के लिए **WhatsApp** खोलें।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **पता:** {{address}}
{{/address}}
{{#url}}
🌐 **वेबसाइट:** {{url}}
{{/url}}
🔗 **मानचित्र पर देखें:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **मतदान**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **कार्यक्रम**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **स्थान:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **आरंभ:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **समाप्ति:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **जुड़ने का लिंक:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - कॉपी और पेस्ट**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX कुंजी:** {{key}}
{{/key}}
{{#keyType}}
**प्रकार:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**राशि:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**संदर्भ:** {{referenceId}}
{{/referenceId}}
💳 **PIX कॉपी और पेस्ट भेजा गया**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **इस प्रकार का संदेश ChatWoot में समर्थित नहीं है।**
📱 इसे देखने के लिए कृपया **WhatsApp** खोलें।
विवरण: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **फेसबुक विज्ञापन**
**शीर्षक:** {{{adData.title}}}
**विवरण:**
{{{adData.body}}}
**विज्ञापन लिंक:** [फेसबुक पर देखें]({{{adData.sourceUrl}}})
**विज्ञापन आईडी:** {{{adData.sourceId}}}
---
💬 **ग्राहक संदेश:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Indonesian translations for ChatWoot integration
locale.name: '🇮🇩 Bahasa Indonesia (Indonesian)'
# Kode locale dasar - harus berupa locale BCP 47 yang valid
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA menggunakannya untuk format angka, mata uang, dan tanggal/waktu
locale.base: 'id-ID'
app.inbox.contact.name: 'Integrasi WhatsApp (WAHA)'
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:
locales: id-ID
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Jakarta'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Lokasi yang dibagikan**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Alamat:** {{address}}
{{/address}}
{{#url}}
🌐 **Situs web:** {{url}}
{{/url}}
🔗 **Lihat di peta:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Berbagi Lokasi Langsung**
Buka **WhatsApp** untuk melihat pembaruan waktu nyata.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Alamat:** {{address}}
{{/address}}
{{#url}}
🌐 **Situs web:** {{url}}
{{/url}}
🔗 **Lihat di peta:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Jajak Pendapat**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Acara**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Lokasi:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Mulai:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Selesai:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Tautan bergabung:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Salin dan Tempel**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Kunci PIX:** {{key}}
{{/key}}
{{#keyType}}
**Jenis:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Jumlah:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Referensi:** {{referenceId}}
{{/referenceId}}
💳 **PIX Salin dan Tempel terkirim**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Jenis pesan ini tidak didukung di ChatWoot.**
📱 Silakan buka **WhatsApp** untuk melihatnya.
Detail: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **IKLAN FACEBOOK**
**Judul:** {{{adData.title}}}
**Deskripsi:**
{{{adData.body}}}
**Tautan Iklan:** [Lihat di Facebook]({{{adData.sourceUrl}}})
**ID Iklan:** {{{adData.sourceId}}}
---
💬 **Pesan Pelanggan:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Punjabi translations for ChatWoot integration
locale.name: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
# ਅਧਾਰਭੂਤ ਲੋਕੇਲ ਕੋਡ - ਇਹ ਇੱਕ ਵੈਧ BCP 47 ਲੋਕੇਲ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA ਇਸਨੂੰ ਅੰਕ, ਕਰੰਸੀ ਅਤੇ ਤਾਰੀਖ/ਸਮੇਂ ਦੀ ਨਮਾਇਸ਼ ਲਈ ਵਰਤਦਾ ਹੈ
locale.base: 'pa-PK'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (WAHA)'
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:
locales: pa-PK
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Karachi'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **ਸਾਂਝੀ ਸਥਿਤੀ**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **ਪਤਾ:** {{address}}
{{/address}}
{{#url}}
🌐 **ਵੈਬਸਾਈਟ:** {{url}}
{{/url}}
🔗 **ਨਕਸ਼ੇ 'ਤੇ ਵੇਖੋ:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **ਲਾਈਵ ਸਥਿਤੀ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ**
ਰੀਅਲ-ਟਾਈਮ ਅੱਪਡੇਟ ਦੇਖਣ ਲਈ **WhatsApp** ਖੋਲ੍ਹੋ।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **ਪਤਾ:** {{address}}
{{/address}}
{{#url}}
🌐 **ਵੈਬਸਾਈਟ:** {{url}}
{{/url}}
🔗 **ਨਕਸ਼ੇ 'ਤੇ ਵੇਖੋ:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **ਸਰਵੇਖਣ**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **ਇਵੈਂਟ**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **ਸਥਾਨ:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **ਸ਼ੁਰੂਆਤ:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **ਖਾਤਮਾ:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **ਜੁੜਨ ਲਈ ਲਿੰਕ:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - ਕਾਪੀ ਅਤੇ ਪੇਸਟ**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX ਕੁੰਜੀ:** {{key}}
{{/key}}
{{#keyType}}
**ਕਿਸਮ:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**ਰਕਮ:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**ਹਵਾਲਾ:** {{referenceId}}
{{/referenceId}}
💳 **PIX ਕਾਪੀ ਅਤੇ ਪੇਸਟ ਭੇਜਿਆ ਗਿਆ**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **ਇਹ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ChatWoot ਵਿੱਚ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।**
📱 ਇਸਨੂੰ ਦੇਖਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ **WhatsApp** ਖੋਲ੍ਹੋ।
ਵੇਰਵੇ: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ਫੇਸਬੁੱਕ ਵਿਗਿਆਪਨ**
**ਸਿਰਲੇਖ:** {{{adData.title}}}
**ਵੇਰਵਾ:**
{{{adData.body}}}
**ਵਿਗਿਆਪਨ ਲਿੰਕ:** [ਫੇਸਬੁੱਕ 'ਤੇ ਵੇਖੋ]({{{adData.sourceUrl}}})
**ਵਿਗਿਆਪਨ ਆਈਡੀ:** {{{adData.sourceId}}}
---
💬 **ਗਾਹਕ ਦਾ ਸੁਨੇਹਾ:**
{{{payload.body}}}
+124 -2
View File
@@ -1,8 +1,28 @@
# Portuguese translations for ChatWoot integration
locale.name: '🇧🇷🇵🇹 Português (Portuguese)'
# Código de locale base - deve ser um locale BCP 47 válido
# https://en.wikipedia.org/wiki/IETF_language_tag
# 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.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:
locales: pt-BR
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -219,7 +239,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Localização compartilhada**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Endereço:** {{address}}
{{/address}}
{{#url}}
🌐 **Site:** {{url}}
{{/url}}
🔗 **Ver no mapa:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -227,13 +259,103 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Compartilhando localização ao vivo**
Abra o **WhatsApp** para ver as atualizações em tempo real.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Endereço:** {{address}}
{{/address}}
{{#url}}
🌐 **Site:** {{url}}
{{/url}}
🔗 **Ver no mapa:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Enquete**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Evento**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Local:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Início:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Fim:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Link de participação:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Copia e Cola**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Chave PIX:** {{key}}
{{/key}}
{{#keyType}}
**Tipo:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Valor:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Referência:** {{referenceId}}
{{/referenceId}}
💳 **PIX Copia e Cola enviado**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Esse tipo de mensagem não é suportado no ChatWoot.**
📱 Por favor, abra o **WhatsApp** para visualizá-la.
Detalhes: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **ANÚNCIO DO FACEBOOK**
**Título:** {{{adData.title}}}
**Descrição:**
{{{adData.body}}}
**Link do Anúncio:** [Ver no Facebook]({{{adData.sourceUrl}}})
**ID do Anúncio:** {{{adData.sourceId}}}
---
💬 **MENSAGEM DO CLIENTE:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Russian translations for ChatWoot integration
locale.name: '🇷🇺 Русский (Russian)'
# Базовый код локали - должен быть корректной локалью BCP 47
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA использует его для форматирования чисел, валют и даты/времени
locale.base: 'ru-RU'
app.inbox.contact.name: 'WhatsApp Интеграция (WAHA)'
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:
locales: ru-RU
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Moscow'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Переданное местоположение**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Адрес:** {{address}}
{{/address}}
{{#url}}
🌐 **Сайт:** {{url}}
{{/url}}
🔗 **Открыть на карте:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Делится геопозицией в реальном времени**
Откройте **WhatsApp**, чтобы увидеть обновления в реальном времени.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Адрес:** {{address}}
{{/address}}
{{#url}}
🌐 **Сайт:** {{url}}
{{/url}}
🔗 **Открыть на карте:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Опрос**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Событие**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Место:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Начало:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Окончание:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Ссылка для участия:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Копировать и вставить**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Ключ PIX:** {{key}}
{{/key}}
{{#keyType}}
**Тип:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Сумма:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Референс:** {{referenceId}}
{{/referenceId}}
💳 **PIX Копировать и вставить отправлен**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Этот тип сообщения не поддерживается в ChatWoot.**
📱 Пожалуйста, откройте **WhatsApp**, чтобы просмотреть.
Подробности: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **РЕКЛАМА FACEBOOK**
**Заголовок:** {{{adData.title}}}
**Описание:**
{{{adData.body}}}
**Ссылка на объявление:** [Посмотреть в Facebook]({{{adData.sourceUrl}}})
**Идентификатор объявления:** {{{adData.sourceId}}}
---
💬 **Сообщение клиента:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Turkish translations for ChatWoot integration
locale.name: '🇹🇷 Türkçe (Turkish)'
# Temel yerel kodu - geçerli bir BCP 47 yerel kodu olmalı
# https://en.wikipedia.org/wiki/IETF_language_tag
# 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.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:
locales: tr-TR
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Istanbul'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Paylaşılan Konum**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adres:** {{address}}
{{/address}}
{{#url}}
🌐 **Web sitesi:** {{url}}
{{/url}}
🔗 **Haritada görüntüle:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Canlı Konum Paylaşılıyor**
Gerçek zamanlı güncellemeleri görmek için **WhatsApp’ı** açın.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Adres:** {{address}}
{{/address}}
{{#url}}
🌐 **Web sitesi:** {{url}}
{{/url}}
🔗 **Haritada görüntüle:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Anket**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Etkinlik**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Konum:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Başlangıç:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Bitiş:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Katılma bağlantısı:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Kopyala ve Yapıştır**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX Anahtarı:** {{key}}
{{/key}}
{{#keyType}}
**Tür:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Tutar:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Referans:** {{referenceId}}
{{/referenceId}}
💳 **PIX Kopyala ve Yapıştır gönderildi**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Bu mesaj türü ChatWoot’ta desteklenmiyor.**
📱 Görüntülemek için lütfen **WhatsApp’ı** açın.
Ayrıntılar: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **FACEBOOK İLANI**
**Başlık:** {{{adData.title}}}
**Açıklama:**
{{{adData.body}}}
**İlan Bağlantısı:** [Facebook’ta Görüntüle]({{{adData.sourceUrl}}})
**İlan Kimliği:** {{{adData.sourceId}}}
---
💬 **Müşteri Mesajı:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Ukrainian translations for ChatWoot integration
locale.name: '🇺🇦 Українська (Ukrainian)'
# Базовий код локалі - має бути чинною локаллю BCP 47
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA використовує його для форматування чисел, валют і дати/часу
locale.base: 'uk-UA'
app.inbox.contact.name: 'Інтеграція WhatsApp (WAHA)'
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:
locales: uk-UA
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Europe/Kyiv'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **Надіслана локація**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Адреса:** {{address}}
{{/address}}
{{#url}}
🌐 **Вебсайт:** {{url}}
{{/url}}
🔗 **Переглянути на карті:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **Надсилання геолокації в реальному часі**
Відкрийте **WhatsApp**, щоб побачити оновлення в реальному часі.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **Адреса:** {{address}}
{{/address}}
{{#url}}
🌐 **Вебсайт:** {{url}}
{{/url}}
🔗 **Переглянути на карті:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **Опитування**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **Подія**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **Місце:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **Початок:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **Завершення:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **Посилання для приєднання:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - Копіювати та Вставити**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**Ключ PIX:** {{key}}
{{/key}}
{{#keyType}}
**Тип:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**Сума:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**Референс:** {{referenceId}}
{{/referenceId}}
💳 **PIX Копіювати та Вставити надіслано**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **Цей тип повідомлення не підтримується в ChatWoot.**
📱 Будь ласка, відкрийте **WhatsApp**, щоб переглянути.
Подробиці: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **РЕКЛАМА FACEBOOK**
**Заголовок:** {{{adData.title}}}
**Опис:**
{{{adData.body}}}
**Посилання на рекламу:** [Переглянути у Facebook]({{{adData.sourceUrl}}})
**Ідентифікатор реклами:** {{{adData.sourceId}}}
---
💬 **Повідомлення клієнта:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Urdu translations for ChatWoot integration
locale.name: '🇵🇰 اردو (Urdu)'
# بنیادی لوکل کوڈ - یہ ایک معتبر BCP 47 لوکل ہونا چاہیے
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA اسے نمبرز، کرنسی اور تاریخ/وقت کی نمائندگی کے لیے استعمال کرتا ہے۔
locale.base: 'ur-PK'
app.inbox.contact.name: 'واٹس ایپ انٹیگریشن (WAHA)'
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:
locales: ur-PK
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Karachi'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -216,7 +236,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **شیئر کی گئی لوکیشن**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **پتہ:** {{address}}
{{/address}}
{{#url}}
🌐 **ویب سائٹ:** {{url}}
{{/url}}
🔗 **نقشے پر دیکھیں:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -224,13 +256,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **لائیو لوکیشن شیئر کی جا رہی ہے**
حقیقی وقت کی تازہ کاریوں کے لیے **WhatsApp** کھولیں۔
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **پتہ:** {{address}}
{{/address}}
{{#url}}
🌐 **ویب سائٹ:** {{url}}
{{/url}}
🔗 **نقشے پر دیکھیں:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **رائے شماری**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **ایونٹ**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **مقام:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **آغاز:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **اختتام:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **شمولیت کا لنک:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - کاپی اور پیسٹ**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX کلید:** {{key}}
{{/key}}
{{#keyType}}
**قسم:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**رقم:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**حوالہ:** {{referenceId}}
{{/referenceId}}
💳 **PIX کاپی اور پیسٹ بھیج دیا گیا**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **ChatWoot میں اس قسم کا پیغام معاونت یافتہ نہیں ہے۔**
📱 اسے دیکھنے کے لیے براہ کرم **WhatsApp** کھولیں۔
تفصیلات: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **فیس بک اشتہار**
**عنوان:** {{{adData.title}}}
**تفصیل:**
{{{adData.body}}}
**اشتہار کا لنک:** [فیس بک پر دیکھیں]({{{adData.sourceUrl}}})
**اشتہار کا شناختی نمبر:** {{{adData.sourceId}}}
---
💬 **گاہک کا پیغام:**
{{{payload.body}}}
+120 -2
View File
@@ -1,8 +1,28 @@
# Simplified Chinese translations for ChatWoot integration
locale.name: '🇨🇳 中文(Simplified Chinese)'
# 基础语言代码 - 必须是有效的 BCP 47 语言区域
# https://en.wikipedia.org/wiki/IETF_language_tag
# WAHA 使用它来格式化数字、货币和日期时间。
locale.base: 'zh-CN'
app.inbox.contact.name: 'WhatsApp 集成(WAHA)'
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:
locales: zh-CN
day: 2-digit
month: 2-digit
year: numeric
hour: 2-digit
minute: 2-digit
# 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: 'Asia/Shanghai'
job.report.error: |-
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
@@ -218,7 +238,19 @@ whatsapp.to.chatwoot.message.location: |-
{{#locationMessage}}
📍 **共享位置**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **地址:** {{address}}
{{/address}}
{{#url}}
🌐 **网站:** {{url}}
{{/url}}
🔗 **在地图上查看:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
@@ -226,13 +258,99 @@ whatsapp.to.chatwoot.message.location: |-
📍📡 **正在共享实时位置**
打开 **WhatsApp** 查看实时更新。
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#name}}
**{{name}}**
{{/name}}
{{#address}}
📍 **地址:** {{address}}
{{/address}}
{{#url}}
🌐 **网站:** {{url}}
{{/url}}
🔗 **在地图上查看:** https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.poll: |-
{{#poll}}
📊 **投票**
**{{name}}**
{{#options}}
- {{optionName}}
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
📅 **活动**
**{{name}}**
{{#description}}
{{description}}
{{/description}}
{{#location.name}}
📍 **地点:** {{location.name}}
{{/location.name}}
{{#startTime}}
🕐 **开始时间:** {{startTime}}
{{/startTime}}
{{#endTime}}
🕐 **结束时间:** {{endTime}}
{{/endTime}}
{{#joinLink}}
🔗 **加入链接:** [{{joinLink}}]({{joinLink}})
{{/joinLink}}
{{/eventMessage}}
{{/message}}
whatsapp.to.chatwoot.message.pix: |-
💳 **PIX - 复制粘贴**
{{#pixData}}
{{#merchantName}}
**{{merchantName}}**
{{/merchantName}}
{{#key}}
**PIX 密钥:** {{key}}
{{/key}}
{{#keyType}}
**类型:** {{keyType}}
{{/keyType}}
{{#totalAmount}}
**金额:** {{totalAmountFormatted}}
{{/totalAmount}}
{{#referenceId}}
**参考:** {{referenceId}}
{{/referenceId}}
💳 **PIX 复制粘贴已发送**
{{/pixData}}
whatsapp.to.chatwoot.message.unsupported: |-
⚠️ **ChatWoot 不支持此消息类型。**
📱 请打开 **WhatsApp** 查看。
详情: [{{{details.text}}}]({{{details.url}}})
whatsapp.to.chatwoot.message.facebook.ad: |-
📢 **Facebook 广告**
**标题:** {{{adData.title}}}
**描述:**
{{{adData.body}}}
**广告链接:** [在 Facebook 查看]({{{adData.sourceUrl}}})
**广告 ID:** {{{adData.sourceId}}}
---
💬 **客户消息:**
{{{payload.body}}}
+52
View File
@@ -51,7 +51,11 @@ export enum TKey {
WA_TO_CW_MESSAGE = 'whatsapp.to.chatwoot.message',
WA_TO_CW_MESSAGE_CONTACTS = 'whatsapp.to.chatwoot.message.contacts',
WA_TO_CW_MESSAGE_LOCATION = 'whatsapp.to.chatwoot.message.location',
WA_TO_CW_MESSAGE_POLL = 'whatsapp.to.chatwoot.message.poll',
WA_TO_CW_MESSAGE_EVENT = 'whatsapp.to.chatwoot.message.event',
WA_TO_CW_MESSAGE_PIX = 'whatsapp.to.chatwoot.message.pix',
WA_TO_CW_MESSAGE_UNSUPPORTED = 'whatsapp.to.chatwoot.message.unsupported',
WA_TO_CW_MESSAGE_FACEBOOK_AD = 'whatsapp.to.chatwoot.message.facebook.ad',
//
// App Inbox
@@ -99,6 +103,34 @@ interface Link {
url: string;
}
export interface FacebookAdTemplateData {
title: string;
body: string;
thumbnailUrl: string;
originalImageUrl: string;
sourceUrl: string;
sourceId: string;
}
export interface PollOption {
optionName?: string;
}
export interface PollCreationMessage {
name?: string;
options?: Array<PollOption>;
}
export interface PixTemplatePayload {
merchantName?: string;
key?: string;
keyType?: string;
currency?: string;
totalAmount?: number;
totalAmountFormatted?: string;
referenceId?: string;
}
export type TemplatePayloads = {
[TKey.LOCALE_NAME]: void;
[TKey.APP_INBOX_CONTACT_NAME]: void;
@@ -131,7 +163,27 @@ export type TemplatePayloads = {
};
[TKey.WA_TO_CW_MESSAGE_CONTACTS]: { contacts: SimpleVCardInfo[] };
[TKey.WA_TO_CW_MESSAGE_LOCATION]: { payload: any; message: proto.Message };
[TKey.WA_TO_CW_MESSAGE_POLL]: {
payload: WAMessage;
poll: PollCreationMessage;
message: proto.Message;
};
[TKey.WA_TO_CW_MESSAGE_EVENT]: {
payload: WAMessage;
message: {
eventMessage: Record<string, unknown>;
};
};
[TKey.WA_TO_CW_MESSAGE_PIX]: {
payload: WAMessage;
message: proto.Message | null;
pixData: PixTemplatePayload;
};
[TKey.WA_TO_CW_MESSAGE_UNSUPPORTED]: { details: Link };
[TKey.WA_TO_CW_MESSAGE_FACEBOOK_AD]: {
payload: WAMessage;
adData: FacebookAdTemplateData;
};
[TKey.JOB_SCHEDULED_ERROR_HEADER]: void;
[TKey.JOB_REPORT_ERROR]: {
header: string;
@@ -0,0 +1,45 @@
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
export class EventMessage implements MessageToChatWootConverter {
constructor(private readonly l: Locale) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
const eventMessage = protoMessage?.eventMessage;
if (!eventMessage) {
return null;
}
const formattedEventMessage = {
...eventMessage,
startTime: this.l.FormatTimestamp(eventMessage.startTime),
endTime: this.l.FormatTimestamp(eventMessage.endTime),
};
const content = this.l.key(TKey.WA_TO_CW_MESSAGE_EVENT).r({
payload,
message: {
eventMessage: formattedEventMessage,
},
});
if (isEmptyString(content)) {
return null;
}
return {
content: WhatsappToMarkdown(content),
attachments: [],
private: undefined,
};
}
}
@@ -0,0 +1,91 @@
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import {
FacebookAdTemplateData,
TKey,
} from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
import { WAMessage } from '@waha/structures/responses.dto';
import type { proto } from '@adiwajshing/baileys';
import * as lodash from 'lodash';
import { fetchBuffer } from '@waha/core/abc/session.abc';
export class FacebookAdMessage implements MessageToChatWootConverter {
constructor(
private readonly locale: Locale,
private readonly logger: ILogger,
) {}
async convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): Promise<ChatWootMessagePartial | null> {
const adData = this.extractAdData(protoMessage);
if (!adData) {
return null;
}
const content = this.locale.key(TKey.WA_TO_CW_MESSAGE_FACEBOOK_AD).render({
payload,
adData,
});
const attachments = await this.getAttachments(adData);
return {
content: WhatsappToMarkdown(content),
attachments,
private: undefined,
};
}
private extractAdData(protoMessage: proto.Message): FacebookAdTemplateData {
if (
lodash.isEmpty(
protoMessage?.extendedTextMessage?.contextInfo?.externalAdReply,
)
) {
return null;
}
const adReply =
protoMessage?.extendedTextMessage?.contextInfo?.externalAdReply;
return {
title: adReply.title ?? '',
body: adReply.body ?? '',
thumbnailUrl: adReply.thumbnailUrl ?? '',
originalImageUrl: adReply.originalImageUrl ?? '',
sourceUrl: adReply.sourceUrl ?? '',
sourceId: adReply.sourceId ?? '',
};
}
private async getAttachments(
adData: FacebookAdTemplateData,
): Promise<SendAttachment[]> {
const imageUrl = adData.originalImageUrl || adData.thumbnailUrl;
if (!imageUrl) {
return [];
}
try {
this.logger.info(`Downloading Facebook Ad image from '${imageUrl}'...`);
const buffer = await fetchBuffer(imageUrl);
const attachment: SendAttachment = {
content: buffer.toString('base64'),
filename: 'facebook-ad-image.jpg',
encoding: 'base64',
};
this.logger.info(
`Downloaded Facebook Ad image from '${imageUrl}' as '${attachment.filename}'`,
);
return [attachment];
} catch (error) {
const reason = error instanceof Error ? error.message : `${error}`;
this.logger.error(
`Failed to download Facebook Ad image from '${imageUrl}': ${reason}`,
);
return [];
}
}
}
@@ -0,0 +1,37 @@
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { isEmptyString } from './utils/proto';
import * as lodash from 'lodash';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
export class LocationMessage implements MessageToChatWootConverter {
constructor(private readonly locale: Locale) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
const hasLocation = !lodash.isEmpty(protoMessage?.locationMessage);
const hasLiveLocation = !lodash.isEmpty(protoMessage?.liveLocationMessage);
if (!hasLocation && !hasLiveLocation) {
return null;
}
const content = this.locale
.key(TKey.WA_TO_CW_MESSAGE_LOCATION)
.r({ payload, message: protoMessage });
if (isEmptyString(content)) {
return null;
}
return {
content,
attachments: [],
private: undefined,
};
}
}
@@ -0,0 +1,28 @@
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { WAMessage } from '@waha/structures/responses.dto';
import type { proto } from '@adiwajshing/baileys';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
export class MessageEdited implements MessageToChatWootConverter {
constructor(private readonly locale: Locale) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
void protoMessage;
const formatted = WhatsappToMarkdown(payload.body);
const content = this.locale
.key(TKey.MESSAGE_EDITED_IN_WHATSAPP)
.render({ text: formatted });
return {
content,
attachments: [],
private: undefined,
};
}
}
@@ -0,0 +1,250 @@
import { PixMessage } from '@waha/apps/chatwoot/messages/to/chatwoot/PixMessage';
import { resolveProtoMessage } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { WAMessage } from '@waha/structures/responses.dto';
import { JestLogger } from '@waha/apps/app_sdk/jest/JestLogger';
import { i18n } from '@waha/apps/chatwoot/i18n';
describe('PixMessage - pt-BR', () => {
const locale = i18n.locale('pt-BR');
it('GOWS - no total amount', () => {
// Setup
const buttonParamsJson = JSON.stringify({
currency: 'BRL',
total_amount: {
value: 0,
offset: 100,
},
reference_id: '4TB11111111',
type: 'physical-goods',
order: {
status: 'pending',
subtotal: {
value: 0,
offset: 100,
},
order_type: 'ORDER',
items: [
{
name: '',
amount: {
value: 0,
offset: 100,
},
quantity: 0,
sale_amount: {
value: 0,
offset: 100,
},
},
],
},
payment_settings: [
{
type: 'pix_static_code',
pix_static_code: {
merchant_name: 'Merchant Name Here',
key: '+5000000000000',
key_type: 'PHONE',
},
},
],
share_payment_status: false,
referral: 'chat_attachment',
});
const payload = {
id: 'false_559900000001@c.us_FAKEPIXORDER1',
timestamp: 1899003201,
from: '559900000001@c.us',
fromMe: false,
source: 'app',
to: null,
participant: null,
hasMedia: false,
ack: 2,
ackName: 'DEVICE',
body: '',
_data: {
Message: {
messageContextInfo: {
deviceListMetadata: {
senderKeyHash: 'aFakeSenderKeyHash==',
senderTimestamp: 1880001234,
recipientKeyHash: 'anotherFakeRecipientKeyHash==',
recipientTimestamp: 1880005678,
},
deviceListMetadataVersion: 2,
messageSecret: 'fakeMessageSecret==',
},
interactiveMessage: {
InteractiveMessage: {
NativeFlowMessage: {
buttons: [
{
name: 'payment_info',
buttonParamsJSON: buttonParamsJson,
},
],
},
},
},
contextInfo: {
expiration: 0,
ephemeralSettingTimestamp: 1880006789,
disappearingMode: {
initiator: 0,
trigger: 0,
},
},
},
},
} as unknown as WAMessage;
// Test
const logger = JestLogger();
const protoMessage = resolveProtoMessage(payload);
const converter = new PixMessage(locale, logger);
const result = converter.convert(payload, protoMessage);
// Assert
const expected = `💳 ****PIX - Copia e Cola****
****Merchant Name Here****
****Chave PIX:**** +5000000000000
****Tipo:**** PHONE
****Referência:**** 4TB11111111
💳 ****PIX Copia e Cola enviado****
`;
expect(protoMessage).not.toBeNull();
expect(result).not.toBeNull();
expect(result.content).toEqual(expected);
expect(logger.fatal).not.toHaveBeenCalled();
expect(logger.error).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
it('GOWS - with total amount', () => {
// Setup
const buttonParamsJson = JSON.stringify({
currency: 'BRL',
total_amount: {
value: 2512,
offset: 100,
},
reference_id: '4TB11111111',
type: 'physical-goods',
order: {
status: 'pending',
subtotal: {
value: 2512,
offset: 100,
},
order_type: 'ORDER',
items: [
{
name: '',
amount: {
value: 2512,
offset: 100,
},
quantity: 1,
sale_amount: {
value: 2512,
offset: 100,
},
},
],
},
payment_settings: [
{
type: 'pix_static_code',
pix_static_code: {
merchant_name: 'Merchant Name Here',
key: '+5000000000000',
key_type: 'PHONE',
},
},
],
share_payment_status: false,
referral: 'chat_attachment',
});
const payload = {
id: 'false_559900000001@c.us_FAKEPIXORDER1',
timestamp: 1899003201,
from: '559900000001@c.us',
fromMe: false,
source: 'app',
to: null,
participant: null,
hasMedia: false,
ack: 2,
ackName: 'DEVICE',
body: '',
_data: {
Message: {
messageContextInfo: {
deviceListMetadata: {
senderKeyHash: 'aFakeSenderKeyHash==',
senderTimestamp: 1880001234,
recipientKeyHash: 'anotherFakeRecipientKeyHash==',
recipientTimestamp: 1880005678,
},
deviceListMetadataVersion: 2,
messageSecret: 'fakeMessageSecret==',
},
interactiveMessage: {
InteractiveMessage: {
NativeFlowMessage: {
buttons: [
{
name: 'payment_info',
buttonParamsJSON: buttonParamsJson,
},
],
},
},
},
contextInfo: {
expiration: 0,
ephemeralSettingTimestamp: 1880006789,
disappearingMode: {
initiator: 0,
trigger: 0,
},
},
},
},
} as unknown as WAMessage;
// Test
const logger = JestLogger();
const protoMessage = resolveProtoMessage(payload);
const converter = new PixMessage(locale, logger);
const result = converter.convert(payload, protoMessage);
// Assert
const expected = `💳 ****PIX - Copia e Cola****
****Merchant Name Here****
****Chave PIX:**** +5000000000000
****Tipo:**** PHONE
****Valor:**** R$\u00A025,12
****Referência:**** 4TB11111111
💳 ****PIX Copia e Cola enviado****
`;
expect(protoMessage).not.toBeNull();
expect(result).not.toBeNull();
expect(result.content).toEqual(expected);
expect(logger.fatal).not.toHaveBeenCalled();
expect(logger.error).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,124 @@
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { PixTemplatePayload, TKey } from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
import * as lodash from 'lodash';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
export class PixMessage implements MessageToChatWootConverter {
constructor(
private readonly l: Locale,
private readonly logger: ILogger,
) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
if (!protoMessage) {
return null;
}
const pixData = this.extractPixData(protoMessage);
if (!pixData) {
return null;
}
const content = this.l.key(TKey.WA_TO_CW_MESSAGE_PIX).r({
payload: payload,
message: protoMessage,
pixData: pixData,
});
if (isEmptyString(content)) {
return null;
}
return {
content: WhatsappToMarkdown(content),
attachments: [],
private: undefined,
};
}
private extractPixData(
protoMessage: proto.Message,
): PixTemplatePayload | null {
const buttons = this.resolveNativeFlowButtons(protoMessage);
if (!buttons || buttons.length === 0) {
return null;
}
const paymentButton = buttons.find(
(button) => button?.name === 'payment_info' && button?.buttonParamsJson,
);
if (!paymentButton?.buttonParamsJson) {
return null;
}
let params: any;
try {
params = JSON.parse(paymentButton.buttonParamsJson);
} catch (error) {
this.logger.warn(`Failed to parse PIX button params: ${error}`);
return null;
}
const pixSettings = params?.payment_settings?.find(
(setting: any) => setting?.type === 'pix_static_code',
);
const pixCode = pixSettings?.pix_static_code;
if (!pixCode) {
return null;
}
const totalAmountFormatted = this.l.FormatCurrency(
params?.currency,
params?.total_amount?.value,
params?.total_amount?.offset,
);
const data: PixTemplatePayload = {
merchantName: pixCode.merchant_name,
key: pixCode.key,
keyType: pixCode.key_type,
currency: params?.currency,
totalAmount: params?.total_amount?.value,
totalAmountFormatted: totalAmountFormatted,
referenceId: params?.reference_id,
};
if (lodash.isEmpty(data)) {
return null;
}
return data;
}
private resolveNativeFlowButtons(
protoMessage: proto.Message,
):
| proto.Message.InteractiveMessage.NativeFlowMessage.INativeFlowButton[]
| null {
let interactiveMessage: proto.Message.IInteractiveMessage =
protoMessage?.interactiveMessage;
if (lodash.isEmpty(interactiveMessage)) {
return null;
}
// GOWS has weird oneOf protobuf behaviour,
// so we need to double interactiveMessage
if (
!lodash.isEmpty(
(interactiveMessage as proto.Message.InteractiveMessage)
.interactiveMessage,
)
) {
interactiveMessage = (
interactiveMessage as proto.Message.InteractiveMessage
).interactiveMessage as proto.Message.IInteractiveMessage;
}
return interactiveMessage?.nativeFlowMessage?.buttons;
}
}
@@ -0,0 +1,46 @@
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { PollCreationMessage, TKey } from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
import * as lodash from 'lodash';
export class PollMessage implements MessageToChatWootConverter {
constructor(private readonly locale: Locale) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
let poll: PollCreationMessage = null;
if (!lodash.isEmpty(protoMessage?.pollCreationMessage)) {
poll = protoMessage.pollCreationMessage;
}
if (!lodash.isEmpty(protoMessage?.pollCreationMessageV2)) {
poll = protoMessage.pollCreationMessageV2;
}
if (!lodash.isEmpty(protoMessage?.pollCreationMessageV3)) {
poll = protoMessage.pollCreationMessageV3;
}
if (!poll) {
return null;
}
const content = this.locale
.key(TKey.WA_TO_CW_MESSAGE_POLL)
.r({ payload: payload, poll: poll, message: protoMessage });
if (isEmptyString(content)) {
return null;
}
return {
content: WhatsappToMarkdown(content),
attachments: [],
private: undefined,
};
}
}
@@ -0,0 +1,70 @@
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { parseVCardV3, SimpleVCardInfo } from '@waha/core/vcard';
import * as lodash from 'lodash';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
export class ShareContactMessage implements MessageToChatWootConverter {
constructor(
private readonly locale: Locale,
private readonly logger: ILogger,
) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
void payload;
const vcards = collectVcards(protoMessage);
if (vcards.length === 0) {
return null;
}
const attachments: SendAttachment[] = vcards.map((vcard, index) => ({
content: Buffer.from(vcard, 'utf8').toString('base64'),
encoding: 'base64',
filename: `vcard-${index + 1}.vcf`,
}));
let contacts: SimpleVCardInfo[] = [];
try {
contacts = vcards.map(parseVCardV3);
} catch (err) {
this.logger.error(
`Error parsing some vcards: vcards=${vcards}, err=${err}`,
);
}
if (contacts.length === 0 && attachments.length === 0) {
return null;
}
const content = this.locale
.key(TKey.WA_TO_CW_MESSAGE_CONTACTS)
.r({ contacts });
return {
content,
attachments,
private: undefined,
};
}
}
function collectVcards(message: proto.Message | null): string[] {
if (lodash.isEmpty(message?.contactsArrayMessage?.contacts)) {
if (!lodash.isEmpty(message?.contactMessage?.vcard)) {
return [message!.contactMessage!.vcard];
}
return [];
}
return message!.contactsArrayMessage!.contacts.map(
(contact) => contact.vcard,
);
}
@@ -0,0 +1,67 @@
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import { isEmptyString } from './utils/proto';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mime = require('mime-types');
export class TextMessage implements MessageToChatWootConverter {
constructor(
private readonly locale: Locale,
private readonly logger: ILogger,
private readonly waha: WAHASelf,
) {}
async convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): Promise<ChatWootMessagePartial | null> {
void protoMessage;
const attachments = await this.getAttachments(payload);
let content = this.locale.key(TKey.WA_TO_CW_MESSAGE).render({ payload });
if (isEmptyString(content) && attachments.length === 0) {
return null;
}
if (isEmptyString(content)) {
content = null;
}
return {
content: WhatsappToMarkdown(content),
attachments,
private: undefined,
};
}
private async getAttachments(payload: WAMessage): Promise<SendAttachment[]> {
const hasMedia = payload.media?.url;
if (!hasMedia) {
return [];
}
const media = payload.media!;
this.logger.info(`Downloading media from '${media.url}'...`);
const buffer = await this.waha.fetch(media.url);
const fileContent = buffer.toString('base64');
let filename = media.filename;
if (!filename) {
const extension = mime.extension(media.mimetype);
filename = `no-filename.${extension}`;
}
const attachment: SendAttachment = {
content: fileContent,
filename,
encoding: 'base64',
};
this.logger.info(`Downloaded media from '${media.url}' as '${filename}'`);
return [attachment];
}
}
@@ -0,0 +1,32 @@
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { Job } from 'bullmq';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
export class UnsupportedMessage implements MessageToChatWootConverter {
constructor(
private readonly locale: Locale,
private readonly job: Job,
) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial {
void payload;
void protoMessage;
const content = this.locale
.key(TKey.WA_TO_CW_MESSAGE_UNSUPPORTED)
.render({ details: JobLink(this.job) });
return {
content,
attachments: [],
private: true,
};
}
}
@@ -0,0 +1,23 @@
import { WAMessage } from '@waha/structures/responses.dto';
import type { proto } from '@adiwajshing/baileys';
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
export { resolveProtoMessage } from './utils/proto';
export { TextMessage } from './TextMessage';
export { LocationMessage } from './LocationMessage';
export { ShareContactMessage } from './ShareContactMessage';
export { UnsupportedMessage } from './UnsupportedMessage';
export { MessageEdited } from './MessageEdited';
export { FacebookAdMessage } from './FacebookAdMessage';
export { PollMessage } from './PollMessage';
export { EventMessage } from './EventMessage';
export { PixMessage } from './PixMessage';
export type Awaitable<T> = T | Promise<T>;
export interface MessageToChatWootConverter {
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): Awaitable<ChatWootMessagePartial | null>;
}
@@ -0,0 +1,17 @@
import { WhatsappToMarkdown } from './markdown';
describe('WhatsappToMarkdown', () => {
it('converts asterisks to bold', () => {
const input = '*bold*';
expect(WhatsappToMarkdown(input)).toBe('**bold**');
});
it('converts underscores to italic', () => {
const input = '_italic_';
expect(WhatsappToMarkdown(input)).toBe('*italic*');
});
it('handles multiple transformations at once', () => {
const input = `*bold* | _italic_ | ~strike~ | \`one line code\` | \`\`\`\nmultiple lines\`\`\` `;
const expected = `**bold** | *italic* | ~~strike~~ | \`one line code\` | \`\`\`\nmultiple lines\`\`\` `;
expect(WhatsappToMarkdown(input)).toBe(expected);
});
});
@@ -0,0 +1,17 @@
export function WhatsappToMarkdown(text: string): string {
if (!text) {
return text;
}
if (text == '') {
return '';
}
return (
text
// Bold: *bold* → **bold**
.replace(/\*(.*?)\*/g, '**$1**')
// Strikethrough: ~strike~ → ~~strike~~
.replace(/~(.*?)~/g, '~~$1~~')
// Italic: _italic_ → *italic*
.replace(/_(.*?)_/g, '*$1*')
);
}
@@ -0,0 +1,55 @@
import { camelCaseKeysDeep } from './proto';
describe('camelCaseKeysDeep', () => {
it('URL to url', () => {
const input = { URL: 'https://example.com/image.png' };
const result = camelCaseKeysDeep(input);
expect(result).toEqual({
url: 'https://example.com/image.png',
});
});
it('converts originalImageURL to originalImageUrl', () => {
const input = { originalImageURL: 'https://example.com/image.png' };
const result = camelCaseKeysDeep(input);
expect(result).toEqual({
originalImageUrl: 'https://example.com/image.png',
});
});
it('keeps externalAdReply as externalAdReply', () => {
const input = { externalAdReply: { title: 'Sample' } };
const result = camelCaseKeysDeep(input);
expect(result).toEqual({ externalAdReply: { title: 'Sample' } });
});
it('converts PascalCase keys to pascalCase', () => {
const input = { PascalCase: 'value' };
const result = camelCaseKeysDeep(input);
expect(result).toEqual({ pascalCase: 'value' });
});
it('camel cases keys nested inside arrays while keeping primitive entries intact', () => {
const input = [
{ MixedCaseKey: 'value', inner_object: { AnotherNestedKey: 42 } },
'STRING_VALUE',
10,
];
const result = camelCaseKeysDeep(input);
expect(result).toEqual([
{ mixedCaseKey: 'value', innerObject: { anotherNestedKey: 42 } },
'STRING_VALUE',
10,
]);
});
});
@@ -0,0 +1,38 @@
import { WAMessage } from '@waha/structures/responses.dto';
import type { proto } from '@adiwajshing/baileys';
import * as lodash from 'lodash';
export function camelCaseKeysDeep<T = any>(input: unknown): T {
if (Array.isArray(input)) return input.map(camelCaseKeysDeep) as unknown as T;
if (input && typeof input === 'object') {
const mapped = lodash.mapKeys(input as Record<string, unknown>, (_v, k) =>
lodash.camelCase(k),
);
return lodash.mapValues(mapped, camelCaseKeysDeep) as unknown as T;
}
return input as T;
}
export function resolveProtoMessage(payload: WAMessage): proto.Message | null {
// GOWS
if (payload._data.Message) {
const protoMessage = payload._data.Message;
// mediaURL => mediaUrl
// otherAttributes => otherAttributes
return camelCaseKeysDeep(protoMessage);
}
// NOWEB
if (payload._data.message) {
return payload._data.message;
}
// WEBJS - not available
return null;
}
export function isEmptyString(content: string) {
if (!content) {
return true;
}
return content === '' || content === '\n';
}
@@ -1,4 +1,4 @@
import { MarkdownToWhatsApp, WhatsappToMarkdown } from './text';
import { MarkdownToWhatsApp } from './markdown';
describe('MarkdownToWhatsApp', () => {
it('converts triple-backtick blocks', () => {
@@ -15,19 +15,3 @@ describe('MarkdownToWhatsApp', () => {
expect(MarkdownToWhatsApp(input)).toBe(expected);
});
});
describe('WhatsappToMarkdown', () => {
it('converts asterisks to bold', () => {
const input = '*bold*';
expect(WhatsappToMarkdown(input)).toBe('**bold**');
});
it('converts underscores to italic', () => {
const input = '_italic_';
expect(WhatsappToMarkdown(input)).toBe('*italic*');
});
it('handles multiple transformations at once', () => {
const input = `*bold* | _italic_ | ~strike~ | \`one line code\` | \`\`\`\nmultiple lines\`\`\` `;
const expected = `**bold** | *italic* | ~~strike~~ | \`one line code\` | \`\`\`\nmultiple lines\`\`\` `;
expect(WhatsappToMarkdown(input)).toBe(expected);
});
});
@@ -23,21 +23,3 @@ export function MarkdownToWhatsApp(text: string): string {
.replace(/^[-+*] (.*)/gm, '* $1')
);
}
export function WhatsappToMarkdown(text: string): string {
if (!text) {
return text;
}
if (text == '') {
return '';
}
return (
text
// Bold: *bold* → **bold**
.replace(/\*(.*?)\*/g, '**$1**')
// Strikethrough: ~strike~ → ~~strike~~
.replace(/~(.*?)~/g, '~~$1~~')
// Italic: _italic_ → *italic*
.replace(/_(.*?)_/g, '*$1*')
);
}
@@ -119,12 +119,15 @@ export class ChatWootAppService implements IAppService {
await conversation.incoming(updated);
}
async beforeSessionStart(
app: App<ChatWootAppConfig>,
session: WhatsappSession,
) {
beforeSessionStart(app: App<ChatWootAppConfig>, session: WhatsappSession) {
this.chatWootWAHAQueueService.listenEvents(app.id, session);
await this.chatWootScheduleService.schedule(app.id, session.name);
}
afterSessionStart(app: App<ChatWootAppConfig>, session: WhatsappSession) {
this.chatWootScheduleService.schedule(app.id, session.name).catch((err) => {
this.logger.error('Error scheduling session for ChatWoot');
this.logger.error(err, err.stack);
});
}
private async setupCustomAttributes(app: App<ChatWootAppConfig>) {
@@ -23,6 +23,8 @@ export class ChatWootWAHAQueueService {
private readonly queueMessageEdited: Queue,
@InjectQueue(QueueName.WAHA_MESSAGE_REVOKED)
private readonly queueMessageRevoked: Queue,
@InjectQueue(QueueName.WAHA_MESSAGE_ACK)
private readonly queueMessageAck: Queue,
@InjectQueue(QueueName.WAHA_SESSION_STATUS)
private readonly queueSessionStatus: Queue,
) {}
@@ -42,6 +44,8 @@ export class ChatWootWAHAQueueService {
return this.queueMessageEdited;
case WAHAEvents.MESSAGE_REVOKED:
return this.queueMessageRevoked;
case WAHAEvents.MESSAGE_ACK:
return this.queueMessageAck;
case WAHAEvents.SESSION_STATUS:
return this.queueSessionStatus;
default:
-4
View File
@@ -104,10 +104,6 @@ export abstract class SessionManager
);
}
async configureApps(session: WhatsappSession) {
await this.appsService.beforeSessionStart(session, this.store);
}
//
// API Methods
//
+17 -4
View File
@@ -43,6 +43,7 @@ import { complete } from '@waha/utils/reactive/complete';
import { SwitchObservable } from '@waha/utils/reactive/SwitchObservable';
import axios from 'axios';
import axiosRetry from 'axios-retry';
import { Agent as HttpsAgent } from 'https';
import * as fs from 'fs';
import * as lodash from 'lodash';
import * as NodeCache from 'node-cache';
@@ -53,7 +54,6 @@ import {
delay,
filter,
of,
pairwise,
retry,
scan,
share,
@@ -1076,11 +1076,24 @@ export abstract class WhatsappSession {
/**
* Fetches the content from the specified URL and returns it as a Buffer.
*/
public async fetch(url: string): Promise<Buffer> {
return axios.get(url, { responseType: 'arraybuffer' }).then((res) => {
public fetch(url: string): Promise<Buffer> {
return fetchBuffer(url);
}
}
const InsecureHttpsAgent = new HttpsAgent({
rejectUnauthorized: false,
});
export async function fetchBuffer(url: string): Promise<Buffer> {
return axios
.get(url, {
responseType: 'arraybuffer',
httpsAgent: InsecureHttpsAgent,
})
.then((res) => {
return Buffer.from(res.data);
});
}
}
export function getGroupInviteLink(code: string) {
+2 -1
View File
@@ -943,9 +943,10 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
async checkNumberStatus(
request: CheckNumberStatusQuery,
): Promise<WANumberExistResult> {
let phone = request.phone.replace(/\+/g, '');
const req = new messages.CheckPhonesRequest({
session: this.session,
phones: [request.phone],
phones: [phone],
});
const response = await promisify(this.client.CheckPhones)(req);
const data = response.toObject();
+27 -7
View File
@@ -293,7 +293,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async start() {
this.status = WAHASessionStatus.STARTING;
await this.buildClient().catch((err) => {
this.buildClient().catch((err) => {
this.logger.error('Failed to start the client');
this.logger.error(err, err.stack);
this.status = WAHASessionStatus.FAILED;
@@ -641,7 +641,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey;
// we need to fetch the poll creation message to get the poll enc key
const pollMsg = await this.getMessage(creationMsgKey);
const pkey = { ...creationMsgKey };
pkey.remoteJid = null; // try to find message creation by id only
const pollMsg = await this.getMessage(pkey);
if (!pollMsg) {
this.logger.warn(
{ creationMsgKey },
@@ -696,7 +698,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.sock.ev.on('messages.upsert', ({ messages }) => {
const meId = this.sock?.authState?.creds?.me?.id;
for (const message of messages) {
if (!isRealMessage(message, meId)) {
if (!isRealMessage(message)) {
continue;
}
if (message.key.fromMe) {
@@ -827,7 +829,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async checkNumberStatus(
request: CheckNumberStatusQuery,
): Promise<WANumberExistResult> {
const phone = request.phone.split('@')[0];
let phone = request.phone.split('@')[0];
phone = phone.replace(/\+/g, '');
const [result] = await this.sock.onWhatsApp(phone);
if (!result || !result.exists) {
return { numberExists: false };
@@ -989,7 +992,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
force: true,
};
const options = await this.getMessageOptions(request);
const result = await this.sock.sendMessage(chatId, message, options);
const result = await this.sock.sendMessage(chatId, message as any, options);
return this.toWAMessage(result);
}
@@ -2212,6 +2215,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
proto.Message.ProtocolMessage.Type.EPHEMERAL_SYNC_RESPONSE
)
return;
if (
message.message?.protocolMessage?.type ===
proto.Message.ProtocolMessage.Type
.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE
)
return;
const normalizedContent = normalizeMessageContent(message.message);
const contentType = getContentType(normalizedContent);
@@ -2377,7 +2386,16 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
const pollCreationMessageKey = key;
const pollCreationMessage = await this.getMessage(key);
const pkey = { ...key };
pkey.remoteJid = null; // try to find message creation by id only
const pollCreationMessage = await this.getMessage(pkey);
if (!pollCreationMessage) {
this.logger.warn(
{ pollCreationMessageKey },
'poll creation message not found, cannot aggregate votes',
);
return;
}
// Handle updates one by one, so we can get Vote Message for the specific vote
for (const pollUpdate of pollUpdates) {
const votes = getAggregateVotesInPollMessage({
@@ -2416,7 +2434,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return;
}
const pollCreationMessageKey = pollUpdateMessage.pollCreationMessageKey;
const pollCreationMessage = await this.getMessage(pollCreationMessageKey);
const pkey = { ...pollCreationMessageKey };
pkey.remoteJid = null; // try to find message creation by id only
const pollCreationMessage = await this.getMessage(pkey);
if (pollCreationMessage) {
// We found message, so later the engine will issue a message.update message
return;
@@ -315,9 +315,7 @@ export class NowebPersistentStore implements INowebStore {
Object.assign(message, fields);
// In case of revoked messages - remove it
// TODO: May be we should save the flag instead of completely removing the message
const isYetRealMessage =
esm.b.isRealMessage(message, this.socket?.authState?.creds?.me?.id) ||
false;
const isYetRealMessage = esm.b.isRealMessage(message) || false;
if (isYetRealMessage) {
await this.messagesRepo.upsertOne(message);
} else {
@@ -346,8 +346,8 @@ export default (config: BaileysInMemoryStoreConfig) => {
switch (action) {
case 'add':
metadata.participants.push(
...participants.map((id) => ({
id,
...participants.map((participant) => ({
id: participant.id,
isAdmin: false,
isSuperAdmin: false,
})),
@@ -356,7 +356,7 @@ export default (config: BaileysInMemoryStoreConfig) => {
case 'demote':
case 'promote':
for (const participant of metadata.participants) {
if (participants.includes(participant.id)) {
if (participants.find((p) => p.id === participant.id)) {
participant.isAdmin = action === 'promote';
}
}
@@ -364,7 +364,8 @@ export default (config: BaileysInMemoryStoreConfig) => {
break;
case 'remove':
metadata.participants = metadata.participants.filter(
(p) => !participants.includes(p.id),
(participant) =>
!participants.find((p) => p.id === participant.id),
);
break;
}
@@ -414,7 +415,7 @@ export default (config: BaileysInMemoryStoreConfig) => {
for (const jid in json.messages) {
const list = assertMessageList(jid);
for (const msg of json.messages[jid]) {
list.upsert(esm.b.proto.WebMessageInfo.create(msg), 'append');
list.upsert(esm.b.proto.WebMessageInfo.create(msg) as any, 'append');
}
}
};
+9 -2
View File
@@ -79,12 +79,19 @@ const isObjectALong = (value: any): value is Long => {
);
};
export function ensureNumber(value: number | Long): number {
export function ensureNumber(value: number | Long | string | null): number {
if (!value) {
// @ts-ignore
return value;
}
return typeof value === 'number' ? value : toNumber(value);
if (typeof value === 'string') {
return Number.parseInt(value, 10);
}
if (isObjectALong(value)) {
return toNumber(value);
}
// number
return value;
}
const toNumber = (longValue: Long): number => {
+2 -1
View File
@@ -600,7 +600,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
async checkNumberStatus(
request: CheckNumberStatusQuery,
): Promise<WANumberExistResult> {
const phone = request.phone.split('@')[0];
let phone = request.phone.split('@')[0];
phone = phone.replace(/\+/g, '');
const result = await this.whatsapp.getNumberId(phone);
if (!result) {
return {
+5 -1
View File
@@ -218,11 +218,15 @@ export class SessionManagerCore extends SessionManager implements OnModuleInit {
webhook.configure(session, webhooks);
// Apps
await this.configureApps(session);
await this.appsService.beforeSessionStart(session, this.store);
// start session
await session.start();
logger.info('Session has been started.');
// Apps
await this.appsService.afterSessionStart(session, this.store);
return {
name: session.name,
status: session.status,
+1 -1
View File
@@ -30,7 +30,7 @@ export function getWAHAVersion(): WAHAVersion {
}
export const VERSION: WAHAEnvironment = {
version: '2025.9.7',
version: '2025.10.2',
engine: getEngineName(),
tier: getWAHAVersion(),
browser:
+1 -1
View File
@@ -2,7 +2,7 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.15"
"ref": "v1.0.17"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
+34 -9
View File
@@ -5,17 +5,17 @@ __metadata:
version: 6
cacheKey: 8
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-09-21":
version: 7.0.0-rc.3
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=7198ccbb5f571dd3f7420f112379996bc41fa659"
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-10":
version: 7.0.0-rc.5
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=dad2730017ab6a24a48d3010796e8338928d0f43"
dependencies:
"@cacheable/node-cache": ^1.4.0
"@hapi/boom": ^9.1.3
async-mutex: ^0.5.0
axios: ^1.6.0
libsignal: "git+https://github.com/whiskeysockets/libsignal-node"
lru-cache: ^11.1.0
music-metadata: ^11.7.0
p-queue: ^9.0.0
pino: ^9.6
protobufjs: ^7.2.4
ws: ^8.13.0
@@ -31,7 +31,7 @@ __metadata:
optional: true
link-preview-js:
optional: true
checksum: bb8f5d9dac5faca5034d829507f70f4d3b71d821bec4ebeab645d774811b12962c4e0a5625d2cb6040e32cc653879acc8911188e9a558b03bd6d696eaefc68ae
checksum: 453dc477eee6c8df1d9aaaddcebfc5ce8b1cca72e958c6d7b742d0ef4afa3fcf1c6db06aba331d7a99596e64499af8e6ade81de0feadb4369fc567e74ddf7fa9
languageName: node
linkType: hard
@@ -6500,6 +6500,13 @@ __metadata:
languageName: node
linkType: hard
"eventemitter3@npm:^5.0.1":
version: 5.0.1
resolution: "eventemitter3@npm:5.0.1"
checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8
languageName: node
linkType: hard
"events@npm:^3.2.0, events@npm:^3.3.0":
version: 3.3.0
resolution: "events@npm:3.3.0"
@@ -8677,7 +8684,7 @@ __metadata:
languageName: node
linkType: hard
"long@npm:^5.0.0":
"long@npm:^5.0.0, long@npm:^5.3.2":
version: 5.3.2
resolution: "long@npm:5.3.2"
checksum: be215816b563f4ca27ad3677678b53415bc489f9e3466414e54d2d85f5f8e86768547fa58493bacfb363ffc57a664debc83403ccc2178aef0c40aca28bad47c9
@@ -9807,6 +9814,23 @@ __metadata:
languageName: node
linkType: hard
"p-queue@npm:^9.0.0":
version: 9.0.0
resolution: "p-queue@npm:9.0.0"
dependencies:
eventemitter3: ^5.0.1
p-timeout: ^7.0.0
checksum: d9d9567f939cbece7799f5c18be97e2093f38bb8542f3525d75906c0d96ddbe2273043b4e0ffb1878707dffd5facde8cc628913259bb1fb17263f843b995f0cb
languageName: node
linkType: hard
"p-timeout@npm:^7.0.0":
version: 7.0.0
resolution: "p-timeout@npm:7.0.0"
checksum: dbaeaae7186f265ddf11bb0315e8b410feced475f5ae2f5e6018c09dd321398272591c8b97261c4ad9ceecf62449f0efbbf66057c8ad9240482e123897082b41
languageName: node
linkType: hard
"p-try@npm:^2.0.0":
version: 2.2.0
resolution: "p-try@npm:2.2.0"
@@ -12470,7 +12494,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "waha@workspace:."
dependencies:
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-09-21"
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-10"
"@adiwajshing/keyed-db": ^0.2.4
"@aws-sdk/client-s3": ^3.633.0
"@aws-sdk/s3-request-presigner": ^3.633.0
@@ -12537,6 +12561,7 @@ __metadata:
libsignal: "github:devlikeapro/libsignal-node#fork-master"
link-preview-js: ^3.0.4
lodash: ^4.17.21
long: ^5.3.2
mime-types: ^2.1.27
mongodb: 6.9.0
mustache: ^4.2.0
@@ -12672,7 +12697,7 @@ __metadata:
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10":
version: 1.34.1
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=67ed942bfa5b9bc4464d004d1691b7437ead989f"
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=130dbdb5608d9d6add1fe8712dc9e8be6a4bab34"
dependencies:
"@pedroslopez/moduleraid": ^5.0.2
archiver: ^5.3.1
@@ -12690,7 +12715,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: a0852af6f93d776438db5cf8138933b241cd44a6d4a93c245e74cd587d005c08a007d73f9925d1bca8f89991a8567a3dde603d094312b82e620a7f8133c2183e
checksum: fd1fa94beab523b7b0becec27bd626e350805e1cdcefe81b4648e4a1c865fab8eeee9ab5a7c2d72d83969647c108df446ee2ffa2a475a0024acb75007fd96a53
languageName: node
linkType: hard