feat(ChatWoot): send messages from device as messages in ChatWoot - fix #2249
This commit is contained in:
1 parent
8e36c7a14c
commit
ee6a952490
12 files changed
+86
-5
No files matched your search
@@ -13,6 +13,7 @@ import { ChatWootQueueService } from '@waha/apps/chatwoot/services/ChatWootQueue
|
||||
import { SessionManager } from '@waha/core/abc/manager.abc';
|
||||
import { AppRepository } from '@waha/apps/app_sdk/storage/AppRepository';
|
||||
import { CommandPrefix } from '@waha/apps/chatwoot/cli';
|
||||
import { IsExternalEcho } from '@waha/apps/chatwoot/api/webhook.guards';
|
||||
|
||||
@Controller('webhooks/chatwoot/')
|
||||
export class ChatwootWebhookController {
|
||||
@@ -41,8 +42,13 @@ export class ChatwootWebhookController {
|
||||
}
|
||||
|
||||
const isCommandsChat = IsCommandsChat(body);
|
||||
// Ignore private notes (most of them)
|
||||
const deleted = body?.content_attributes?.deleted;
|
||||
// Ignore messages that came from WhatsApp, do not send them back
|
||||
if (IsExternalEcho(body) && !deleted) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// Ignore private notes (most of them)
|
||||
if (body.private) {
|
||||
// Ignore any private note in commands chats
|
||||
if (isCommandsChat) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { conversation_message_create } from '@figuro/chatwoot-sdk';
|
||||
|
||||
/**
|
||||
* Mark a message WAHA creates from a WhatsApp message, so the webhook does not send it back
|
||||
*/
|
||||
export function SetExternalEcho(body: conversation_message_create) {
|
||||
body.content_attributes = { ...body.content_attributes, external_echo: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Message was created by WAHA from a WhatsApp message, not by an agent
|
||||
*/
|
||||
export function IsExternalEcho(body: any): boolean {
|
||||
return Boolean(body?.content_attributes?.external_echo);
|
||||
}
|
||||
@@ -294,6 +294,7 @@ class MessagesPullHandler {
|
||||
this.session,
|
||||
container.Locale(),
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
handler.force = options.force;
|
||||
|
||||
|
||||
@@ -36,10 +36,11 @@ import { EngineHelper } from '@waha/apps/chatwoot/waha';
|
||||
import { EnsureSeconds } from '@waha/utils/timehelper';
|
||||
import { CHATWOOT_MESSAGE_CALENDAR_THRESHOLD_SECONDS } from '@waha/apps/chatwoot/env';
|
||||
import {
|
||||
ChatWootAppConfig,
|
||||
ChatWootConfig,
|
||||
ChatWootOutgoingMode,
|
||||
} from '@waha/apps/chatwoot/dto/config.dto';
|
||||
import { clearContent } from '@waha/apps/chatwoot/consumers/utils';
|
||||
import { SetExternalEcho } from '@waha/apps/chatwoot/api/webhook.guards';
|
||||
|
||||
export function ListenEventsForChatWoot(config: ChatWootConfig) {
|
||||
const events = [
|
||||
@@ -235,6 +236,7 @@ export abstract class MessageBaseHandler<
|
||||
protected session: WAHASessionAPI,
|
||||
protected l: Locale,
|
||||
protected waha: WAHASelf,
|
||||
protected outgoingMode: ChatWootOutgoingMode,
|
||||
) {}
|
||||
|
||||
protected abstract getMessage(
|
||||
@@ -402,10 +404,35 @@ export abstract class MessageBaseHandler<
|
||||
},
|
||||
);
|
||||
|
||||
const private_ = message.private ?? payload.fromMe;
|
||||
const type = private_ ? MessageType.OUTGOING : MessageType.INCOMING;
|
||||
// Send the message as a private note or as a regular message?
|
||||
let private_ = message.private;
|
||||
if (private_ === undefined) {
|
||||
switch (this.outgoingMode) {
|
||||
case ChatWootOutgoingMode.MESSAGE:
|
||||
// Regular outgoing message, not a note
|
||||
private_ = false;
|
||||
break;
|
||||
case ChatWootOutgoingMode.PRIVATE_NOTE:
|
||||
default: // no value - old config, treat it as PRIVATE_NOTE
|
||||
// Mark my messages as private notes
|
||||
private_ = Boolean(payload.fromMe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message as incoming or outgoing?
|
||||
let type: MessageType = MessageType.INCOMING;
|
||||
if (payload.fromMe) {
|
||||
// Any message from me is outgoing, private or not
|
||||
type = MessageType.OUTGOING;
|
||||
}
|
||||
if (private_) {
|
||||
// Private notes can only be sent as outgoing messages
|
||||
type = MessageType.OUTGOING;
|
||||
}
|
||||
|
||||
content = this.finalizeContent(content, payload);
|
||||
return {
|
||||
const body: conversation_message_create = {
|
||||
content: content,
|
||||
message_type: type,
|
||||
private: private_,
|
||||
@@ -414,6 +441,10 @@ export abstract class MessageBaseHandler<
|
||||
in_reply_to: replyTo,
|
||||
},
|
||||
};
|
||||
if (payload.fromMe) {
|
||||
SetExternalEcho(body);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
async getReplyToChatWootMessageID(
|
||||
|
||||
@@ -59,6 +59,7 @@ export class WAHACallAcceptedConsumer extends ChatWootWAHABaseConsumer {
|
||||
new WAHASessionAPI(event.session, container.WAHASelf()),
|
||||
locale,
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
const msg = BuildCallMessagePayload(
|
||||
event.payload,
|
||||
|
||||
@@ -60,6 +60,7 @@ export class WAHACallReceivedConsumer extends ChatWootWAHABaseConsumer {
|
||||
new WAHASessionAPI(event.session, container.WAHASelf()),
|
||||
locale,
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
const msg = BuildCallMessagePayload(
|
||||
event.payload,
|
||||
|
||||
@@ -59,6 +59,7 @@ export class WAHACallRejectedConsumer extends ChatWootWAHABaseConsumer {
|
||||
new WAHASessionAPI(event.session, container.WAHASelf()),
|
||||
locale,
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
const msg = BuildCallMessagePayload(
|
||||
event.payload,
|
||||
|
||||
@@ -65,6 +65,7 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
|
||||
session,
|
||||
container.Locale(),
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
return await handler.handle(event.payload);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
|
||||
session,
|
||||
container.Locale(),
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
return await handler.handle(event.payload);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
|
||||
session,
|
||||
container.Locale(),
|
||||
container.WAHASelf(),
|
||||
container.OutgoingMode(),
|
||||
);
|
||||
return await handler.handle(event.payload);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ChatWootInboxAPI } from '@waha/apps/chatwoot/client/interfaces';
|
||||
import {
|
||||
ChatWootAppConfig,
|
||||
ChatWootConfig,
|
||||
ChatWootOutgoingMode,
|
||||
DEFAULT_LOCALE,
|
||||
LinkPreview,
|
||||
} from '@waha/apps/chatwoot/dto/config.dto';
|
||||
@@ -119,6 +120,10 @@ export class DIContainer {
|
||||
);
|
||||
}
|
||||
|
||||
public OutgoingMode(): ChatWootOutgoingMode {
|
||||
return this.ChatWootConfig().conversations.outgoing;
|
||||
}
|
||||
|
||||
@CacheSync()
|
||||
public ConversationSelector() {
|
||||
const config = this.ChatWootConfig();
|
||||
@@ -229,6 +234,7 @@ export function ChatWootConfigDefaults(
|
||||
sort: ConversationSort.created_newest,
|
||||
status: null,
|
||||
markAsRead: true,
|
||||
outgoing: ChatWootOutgoingMode.PRIVATE_NOTE,
|
||||
},
|
||||
};
|
||||
return lodash.defaultsDeep({}, config, defaults);
|
||||
|
||||
@@ -30,6 +30,11 @@ export enum LinkPreview {
|
||||
HQ = 'HG',
|
||||
}
|
||||
|
||||
export enum ChatWootOutgoingMode {
|
||||
PRIVATE_NOTE = 'private-note',
|
||||
MESSAGE = 'message',
|
||||
}
|
||||
|
||||
export class ChatWootConversationsConfig {
|
||||
@IsEnum(ConversationSort)
|
||||
sort: ConversationSort;
|
||||
@@ -46,6 +51,17 @@ export class ChatWootConversationsConfig {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
markAsRead?: boolean = true;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'How to show messages sent from WhatsApp (not from ChatWoot): ' +
|
||||
"'private-note' (default) or 'message' - a regular outgoing message, as if an agent sent it.",
|
||||
enum: ChatWootOutgoingMode,
|
||||
default: ChatWootOutgoingMode.PRIVATE_NOTE,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(ChatWootOutgoingMode)
|
||||
outgoing?: ChatWootOutgoingMode = ChatWootOutgoingMode.PRIVATE_NOTE;
|
||||
}
|
||||
|
||||
export interface ChatWootConfig {
|
||||
|
||||
Reference in new issue
Block a user