Skip to content

Commit 9f66c9f

Browse files
committed
✨ CLI: added chatwoot incoming media support #2363
1 parent 2bd9ec1 commit 9f66c9f

File tree

3 files changed

+93
-40
lines changed

3 files changed

+93
-40
lines changed

package-lock.json

Lines changed: 53 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"@types/cross-spawn": "^6.0.2",
6868
"@types/death": "^1.1.1",
6969
"@types/express": "^4.17.11",
70+
"@types/form-data": "^2.5.0",
7071
"@types/fs-extra": "^9.0.11",
7172
"@types/line-reader": "0.0.34",
7273
"@types/localtunnel": "^2.0.1",
@@ -119,6 +120,7 @@
119120
"express": "^4.17.1",
120121
"express-robots-txt": "^1.0.0",
121122
"find-up": "^5.0.0",
123+
"form-data": "^4.0.0",
122124
"fs-extra": "^10.0.0",
123125
"get-port": "^5.1.1",
124126
"hasha": "^5.2.0",

src/cli/integrations/chatwoot.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Client, ev, SimpleListener, ChatId, Message, Contact } from '../..';
22
import { app, cliFlags } from '../server';
33
import { Request, Response } from "express";
44
import { default as axios } from 'axios'
5+
import { default as FormData } from 'form-data'
6+
import mime from 'mime-types';
57

68
export type expressMiddleware = (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>
79

@@ -10,6 +12,8 @@ export const chatwootMiddleware: (cliConfig: cliFlags, client: Client) => expres
1012
const processMesssage = async () => {
1113
const promises = [];
1214
const { body } = req
15+
if(!body) return;
16+
if(!body.conversation) return;
1317
const m = body.conversation.messages[0];
1418
const contact = (body.conversation.meta.sender.phone_number || "").replace('+', '')
1519
if (
@@ -69,15 +73,16 @@ export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client:
6973
const [accountId, inboxId] = u.match(/\/(app|(api\/v1))\/accounts\/\d*\/inbox\/\d*/g)[0].split('/').filter(Number)
7074
// const accountId = u.match(/accounts\/\d*/g) && u.match(/accounts\/\d*/g)[0].replace('accounts/', '')
7175
const resolvedInbox = inboxId || u.match(/inboxes\/\d*/g) && u.match(/inboxes\/\d*/g)[0].replace('inboxes/', '')
72-
const cwReq = (path, method, data?: any) => {
76+
const cwReq = (path, method, data?: any, _headers ?: any) => {
7377
const url = `${origin}/api/v1/accounts/${accountId}/${path}`.replace('app.bentonow.com','chat.bentonow.com')
74-
console.log(url,method,data)
78+
// console.log(url,method,data)
7579
return axios({
7680
method,
7781
data,
7882
url,
7983
headers: {
80-
api_access_token
84+
api_access_token,
85+
..._headers
8186
}
8287
})
8388
}
@@ -114,7 +119,7 @@ export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client:
114119
const getContactConversation = async (number: string) => {
115120
try {
116121
const { data } = await cwReq(`contacts/${contactReg[number]}/conversations`, 'get');
117-
return data.payload[0];
122+
return data.payload.sort((a,b)=>a.id-b.id)[0];
118123
} catch (error) {
119124
return;
120125
}
@@ -159,6 +164,26 @@ export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client:
159164
}
160165
}
161166

167+
const sendAttachmentMessage = async (content, contactId, message : Message) => {
168+
// decrypt message
169+
const file = await client.decryptMedia(message)
170+
let formData = new FormData();
171+
formData.append('attachments[]', Buffer.from(file.split(',')[1], 'base64'), {
172+
knownLength: 1,
173+
filename: `${message.t}.${mime.extension(message.mimetype)}`,
174+
contentType: (file.match(/[^:\s*]\w+\/[\w-+\d.]+(?=[;| ])/) || ["application/octet-stream"])[0]
175+
});
176+
formData.append('content', content)
177+
formData.append('message_type', 'incoming')
178+
try {
179+
const { data } = await cwReq(`conversations/${convoReg[contactId]}/messages`, 'post', formData, formData.getHeaders());
180+
return data;
181+
} catch (error) {
182+
return;
183+
}
184+
}
185+
186+
162187

163188
// const inboxId = s.match(/conversations\/\d*/g) && s.match(/conversations\/\d*/g)[0].replace('conversations/','')
164189
/**
@@ -195,6 +220,7 @@ export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client:
195220
* Does the conversation exist in
196221
*/
197222
let text = message.body;
223+
let hasAttachments = false;
198224
switch (message.type) {
199225
case 'location':
200226
text = `${message.lat},${message.lng}`;
@@ -207,12 +233,18 @@ export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client:
207233
case 'audio':
208234
case 'ptt':
209235
case 'video':
210-
if (message.cloudUrl) text = `FILE:\t${message.cloudUrl}\n\nMESSAGE:\t${message.text}`;
236+
if (message.cloudUrl) {
237+
text = `FILE:\t${message.cloudUrl}\n\nMESSAGE:\t${message.text}`;
238+
} else {
239+
text = message.text;
240+
hasAttachments = true;
241+
}
211242
break;
212243
default:
213244
text = message.body || "__UNHANDLED__";
214245
break;
215246
}
216-
await sendConversationMessage(text, message.from, message)
247+
if(hasAttachments) await sendAttachmentMessage(text, message.from, message)
248+
else await sendConversationMessage(text, message.from, message)
217249
})
218250
}

0 commit comments

Comments
 (0)