Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cfb53c367 | ||
|
|
9466407b8a | ||
|
|
4b65f0c462 | ||
|
|
23504b35c8 | ||
|
|
dd6ec4ca3a | ||
|
|
8235e5a111 | ||
|
|
55499a6144 | ||
|
|
9dbc4a6085 | ||
|
|
f7c6ce847c | ||
|
|
84eed00415 | ||
|
|
51aa0e3154 | ||
|
|
77893a0eb0 | ||
|
|
4a866d8a08 | ||
|
|
f912353a5c | ||
|
|
a6a1d8dc28 | ||
|
|
150e73aa74 | ||
|
|
a9300b3134 | ||
|
|
bbe3859085 | ||
|
|
b420548dd9 | ||
|
|
741483eec7 | ||
|
|
2acf7e4e17 | ||
|
|
9bca6d904d | ||
|
|
857c586df6 | ||
|
|
df8cb7cecb | ||
|
|
9d0050adb8 | ||
|
|
df927ab04a | ||
|
|
02e5934e8f | ||
|
|
825a4152db | ||
|
|
81b10099af | ||
|
|
3be5f328b0 | ||
|
|
5caa3b8b81 | ||
|
|
8231bf7d8e | ||
|
|
22d94808fb | ||
|
|
57daefa680 | ||
|
|
5cf8704973 | ||
|
|
28da9c34bb | ||
|
|
51ba5e3266 | ||
|
|
f696794ee1 | ||
|
|
c6eca9510b | ||
|
|
9a2fd2fccd | ||
|
|
c0a2d02f0d | ||
|
|
6493d16fd2 | ||
|
|
9f96c678dc | ||
|
|
a81f2ab553 | ||
|
|
8fbf7fa0e5 | ||
|
|
af0bba313a |
No files matched your search
@@ -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'
|
||||
@@ -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|
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}`,
|
||||
);
|
||||
|
||||
@@ -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,113 @@
|
||||
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;
|
||||
}
|
||||
// Only READ and PLAYED
|
||||
const payload = event.payload;
|
||||
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(),
|
||||
);
|
||||
await handler.handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 [];
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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**
|
||||
|
||||
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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}}}
|
||||
@@ -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
-17
@@ -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:
|
||||
|
||||
@@ -104,10 +104,6 @@ export abstract class SessionManager
|
||||
);
|
||||
}
|
||||
|
||||
async configureApps(session: WhatsappSession) {
|
||||
await this.appsService.beforeSessionStart(session, this.store);
|
||||
}
|
||||
|
||||
//
|
||||
// API Methods
|
||||
//
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -12,19 +12,25 @@ describe('E164Parser.fromJid', () => {
|
||||
expect(E164Parser.fromJid('@s.whatsapp.net')).toBeNull();
|
||||
});
|
||||
|
||||
it('applies Brazil DDD + add-9 rule when local has 8 digits and not starting with 9', () => {
|
||||
// +55 <DDD:2> <local:8> and local does not start with 9
|
||||
it('applies Brazil add-9 rule when local has 8 digits (any first digit)', () => {
|
||||
// +55 <DDD:2> <local:8>
|
||||
expect(E164Parser.fromJid('553188888888@s.whatsapp.net')).toBe(
|
||||
'+5531988888888',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not add 9 when local already starts with 9 (Brazil)', () => {
|
||||
it('554999111111 => 5549999111111', () => {
|
||||
expect(E164Parser.fromJid('554999111111@s.whatsapp.net')).toBe(
|
||||
'+5549999111111',
|
||||
);
|
||||
});
|
||||
|
||||
it('adds 9 even when local already starts with 9 (Brazil)', () => {
|
||||
expect(E164Parser.fromJid('553198888888@s.whatsapp.net')).toBe(
|
||||
'+553198888888',
|
||||
'+5531998888888',
|
||||
);
|
||||
expect(E164Parser.fromJid('553199999999@s.whatsapp.net')).toBe(
|
||||
'+553199999999',
|
||||
'+5531999999999',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -53,12 +53,10 @@ export class PhoneJidNormalizer {
|
||||
}
|
||||
|
||||
const RULES = [
|
||||
// Brazil - add 9 before Direct Distance Dialing
|
||||
// +55 <DDD:2> <local:8>, not already starting with 9
|
||||
// +553188888888 => +5531988888888
|
||||
// Brazil - ensure mobile numbers have 9 digits after DDD
|
||||
{
|
||||
name: 'br-add-9-after-ddd',
|
||||
re: /^\+55(\d{2})(?!9)(\d{8})$/,
|
||||
re: /^\+55(\d{2})(\d{8})$/,
|
||||
replace: '+55$19$2',
|
||||
},
|
||||
];
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export function getWAHAVersion(): WAHAVersion {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.9.6',
|
||||
version: '2025.10.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.15"
|
||||
"ref": "v1.0.17"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
|
||||
@@ -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=9fea6b355c861fb016aad2d6930ecb880f9c63f4"
|
||||
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: dfce2cbd4706f9b61effb9e3cf6b5c8dc4315d5cd6c71622f7d3ed9813bb37e4836feb0a7f80c6a7c6a5f90abe0220d1f71e3fa9a8066ca31e608431496671a9
|
||||
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
|
||||
|
||||
|
||||
Reference in new issue
Block a user