@open-wa/wa-automate 4.25.1 → 4.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -113,6 +113,12 @@ function start() {
113
113
  process.exit();
114
114
  }
115
115
  }));
116
+ if (cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.chatwootUrl) {
117
+ spinner.info('Setting Up Chatwoot handler');
118
+ spinner.info('Make sure to set up the Chatwoot inbox webhook to the following path on this process: /chatwoot');
119
+ yield server_1.setupChatwoot(cliConfig, client);
120
+ spinner.succeed('Chatwoot handler set up successfully');
121
+ }
116
122
  if (cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.botPressUrl) {
117
123
  spinner.info('Setting Up Botpress handler');
118
124
  server_1.setupBotPressHandler(cliConfig, client);
@@ -0,0 +1,6 @@
1
+ import { Client } from '../..';
2
+ import { cliFlags } from '../server';
3
+ import { Request, Response } from "express";
4
+ export declare type expressMiddleware = (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
5
+ export declare const chatwootMiddleware: (cliConfig: cliFlags, client: Client) => expressMiddleware;
6
+ export declare const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client: Client) => Promise<void>;
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.setupChatwootOutgoingMessageHandler = exports.chatwootMiddleware = void 0;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const chatwootMiddleware = (cliConfig, client) => {
18
+ return (req, res) => __awaiter(void 0, void 0, void 0, function* () {
19
+ const processMesssage = () => __awaiter(void 0, void 0, void 0, function* () {
20
+ const promises = [];
21
+ const { body } = req;
22
+ const m = body.conversation.messages[0];
23
+ const contact = (body.conversation.meta.sender.phone_number || "").replace('+', '');
24
+ if (body.message_type === "incoming" ||
25
+ body.private ||
26
+ body.event !== "message_created" ||
27
+ !m ||
28
+ !contact)
29
+ return;
30
+ const { attachments, content } = m;
31
+ const to = `${contact}@c.us`;
32
+ if ((attachments === null || attachments === void 0 ? void 0 : attachments.length) > 0) {
33
+ //has attachments
34
+ const [firstAttachment, ...restAttachments] = attachments;
35
+ const sendAttachment = (attachment, c) => __awaiter(void 0, void 0, void 0, function* () { return client.sendImage(to, attachment.data_url, attachment.data_url.substring(attachment.data_url.lastIndexOf('/') + 1), c || '', null, true); });
36
+ //send the text as the caption with the first message only
37
+ promises.push(sendAttachment(firstAttachment, content));
38
+ restAttachments.map(sendAttachment).map(promises.push);
39
+ }
40
+ else {
41
+ //no attachments
42
+ if (!content)
43
+ return;
44
+ /**
45
+ * Check if this is a location message
46
+ */
47
+ const locationMatcher = /@(\-*\d*\.*\d*\,\-*\d*\.*\d*)/g;
48
+ const [possLoc, ...restMessage] = content.split(' ');
49
+ const locArr = possLoc.match(locationMatcher);
50
+ if (locArr) {
51
+ const [lat, lng] = locArr[0].split(',');
52
+ //grab the location message
53
+ const loc = restMessage.join(' ') || '';
54
+ promises.push(client.sendLocation(to, lat, lng, loc));
55
+ }
56
+ else {
57
+ //not a location message
58
+ promises.push(client.sendText(to, content));
59
+ }
60
+ }
61
+ return yield Promise.all(promises);
62
+ });
63
+ try {
64
+ const processAndSendResult = yield processMesssage();
65
+ console.log("🚀 ~ file: chatwoot.ts ~ line 58 ~ return ~ processAndSendResult", processAndSendResult);
66
+ res.status(200).send(processAndSendResult);
67
+ }
68
+ catch (error) {
69
+ console.log("🚀 ~ file: chatwoot.ts ~ line 62 ~ return ~ error", error);
70
+ res.status(400).send(error);
71
+ }
72
+ return;
73
+ });
74
+ };
75
+ exports.chatwootMiddleware = chatwootMiddleware;
76
+ const setupChatwootOutgoingMessageHandler = (cliConfig, client) => __awaiter(void 0, void 0, void 0, function* () {
77
+ const u = cliConfig.chatwootUrl; //e.g `"localhost:3000/api/v1/accounts/3"
78
+ const api_access_token = cliConfig.chatwootApiAccessToken;
79
+ const _u = new URL(u);
80
+ const origin = _u.origin;
81
+ const port = _u.port || 80;
82
+ const accountId = u.match(/accounts\/\d*/g) && u.match(/accounts\/\d*/g)[0].replace('accounts/', '');
83
+ const resolvedInbox = u.match(/inboxes\/\d*/g) && u.match(/inboxes\/\d*/g)[0].replace('inboxes/', '');
84
+ console.log("🚀 ~ file: chatwoot.ts ~ line 136 ~ resolvedInbox", resolvedInbox);
85
+ const cwReq = (path, method, data) => {
86
+ console.log(`${origin}/api/v1/accounts/${accountId}/${path}`, method, data);
87
+ return axios_1.default({
88
+ method,
89
+ data,
90
+ url: `${origin}/api/v1/accounts/${accountId}/${path}`,
91
+ headers: {
92
+ api_access_token
93
+ }
94
+ });
95
+ };
96
+ const contactReg = {
97
+ //WID : chatwoot contact ID
98
+ "example@c.us": "1"
99
+ };
100
+ const convoReg = {
101
+ //WID : chatwoot conversation ID
102
+ "example@c.us": "1"
103
+ };
104
+ const { data: get_inbox } = yield cwReq(`inboxes/${resolvedInbox}`, 'get');
105
+ // const inboxId = `openwa_${sessionId}`
106
+ /**
107
+ * Get the inbox and test it.
108
+ */
109
+ if (!((get_inbox === null || get_inbox === void 0 ? void 0 : get_inbox.webhook_url) || "").includes("/chatwoot"))
110
+ console.log("Please set the chatwoot inbox webhook to this sessions URL with path /chatwoot");
111
+ /**
112
+ * Get Contacts and conversations
113
+ */
114
+ const searchContact = (number) => __awaiter(void 0, void 0, void 0, function* () {
115
+ try {
116
+ const n = number.replace('@c.us', '');
117
+ const { data } = yield cwReq(`contacts/search?q=${n}&sort=phone_number`, 'get');
118
+ if (data.payload.length) {
119
+ return data.payload.find(x => (x.phone_number || "").includes(n)) || false;
120
+ }
121
+ else
122
+ false;
123
+ }
124
+ catch (error) {
125
+ return;
126
+ }
127
+ });
128
+ const getContactConversation = (number) => __awaiter(void 0, void 0, void 0, function* () {
129
+ try {
130
+ const { data } = yield cwReq(`contacts/${contactReg[number]}/conversations`, 'get');
131
+ return data.payload[0];
132
+ }
133
+ catch (error) {
134
+ return;
135
+ }
136
+ });
137
+ const createConversation = (contact_id) => __awaiter(void 0, void 0, void 0, function* () {
138
+ try {
139
+ const { data } = yield cwReq(`conversations`, 'post', {
140
+ contact_id,
141
+ "inbox_id": resolvedInbox
142
+ });
143
+ return data.payload;
144
+ }
145
+ catch (error) {
146
+ return;
147
+ }
148
+ });
149
+ const createContact = (contact) => __awaiter(void 0, void 0, void 0, function* () {
150
+ try {
151
+ const { data } = yield cwReq(`contacts`, 'post', {
152
+ "identifier": contact.id,
153
+ "name": contact.formattedName || contact.id,
154
+ "phone_number": `+${contact.id.replace('@c.us', '')}`,
155
+ "avatar_url": contact.profilePicThumbObj.eurl
156
+ });
157
+ return data.payload.contact;
158
+ }
159
+ catch (error) {
160
+ return;
161
+ }
162
+ });
163
+ const sendConversationMessage = (content, contactId, message) => __awaiter(void 0, void 0, void 0, function* () {
164
+ try {
165
+ const { data } = yield cwReq(`conversations/${convoReg[contactId]}/messages`, 'post', {
166
+ content,
167
+ "message_type": 0,
168
+ "private": false
169
+ });
170
+ return data;
171
+ }
172
+ catch (error) {
173
+ return;
174
+ }
175
+ });
176
+ // const inboxId = s.match(/conversations\/\d*/g) && s.match(/conversations\/\d*/g)[0].replace('conversations/','')
177
+ /**
178
+ * Update the chatwoot contact and conversation registries
179
+ */
180
+ client.onMessage((message) => __awaiter(void 0, void 0, void 0, function* () {
181
+ if (message.from.includes('g')) {
182
+ //chatwoot integration does not support group chats
183
+ return;
184
+ }
185
+ /**
186
+ * Does the contact exist in chatwoot?
187
+ */
188
+ if (!contactReg[message.from]) {
189
+ const contact = yield searchContact(message.from);
190
+ if (contact) {
191
+ contactReg[message.from] = contact.id;
192
+ }
193
+ else {
194
+ //create the contact
195
+ contactReg[message.from] = (yield createContact(message.sender)).id;
196
+ }
197
+ }
198
+ if (!convoReg[message.from]) {
199
+ const conversation = yield getContactConversation(message.from);
200
+ if (conversation) {
201
+ convoReg[message.from] = conversation.id;
202
+ }
203
+ else {
204
+ //create the conversation
205
+ convoReg[message.from] = (yield createConversation(contactReg[message.from])).id;
206
+ }
207
+ }
208
+ /**
209
+ * Does the conversation exist in
210
+ */
211
+ let text = message.body;
212
+ switch (message.type) {
213
+ case 'location':
214
+ text = `${message.lat},${message.lng}`;
215
+ break;
216
+ case 'buttons_response':
217
+ text = message.selectedButtonId;
218
+ break;
219
+ case 'document':
220
+ case 'image':
221
+ case 'audio':
222
+ case 'ptt':
223
+ case 'video':
224
+ if (message.cloudUrl)
225
+ text = `FILE:\t${message.cloudUrl}\n\nMESSAGE:\t${message.text}`;
226
+ break;
227
+ default:
228
+ text = message.body || "__UNHANDLED__";
229
+ break;
230
+ }
231
+ yield sendConversationMessage(text, message.from, message);
232
+ }));
233
+ });
234
+ exports.setupChatwootOutgoingMessageHandler = setupChatwootOutgoingMessageHandler;
@@ -16,5 +16,6 @@ export declare const getCommands: () => any;
16
16
  export declare const listListeners: () => string[];
17
17
  export declare const setupMediaMiddleware: () => void;
18
18
  export declare const setupTwilioCompatibleWebhook: (cliConfig: cliFlags, client: Client) => void;
19
+ export declare const setupChatwoot: (cliConfig: cliFlags, client: Client) => void;
19
20
  export declare const setupBotPressHandler: (cliConfig: cliFlags, client: Client) => void;
20
21
  export declare const setupSocketServer: (cliConfig: any, client: Client) => Promise<void>;
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.setupSocketServer = exports.setupBotPressHandler = exports.setupTwilioCompatibleWebhook = exports.setupMediaMiddleware = exports.listListeners = exports.getCommands = exports.setupRefocusDisengageMiddleware = exports.setupSwaggerStatsMiddleware = exports.setupApiDocs = exports.setupAuthenticationLayer = exports.enableCORSRequests = exports.setUpExpressApp = exports.server = exports.app = void 0;
34
+ exports.setupSocketServer = exports.setupBotPressHandler = exports.setupChatwoot = exports.setupTwilioCompatibleWebhook = exports.setupMediaMiddleware = exports.listListeners = exports.getCommands = exports.setupRefocusDisengageMiddleware = exports.setupSwaggerStatsMiddleware = exports.setupApiDocs = exports.setupAuthenticationLayer = exports.enableCORSRequests = exports.setUpExpressApp = exports.server = exports.app = void 0;
35
35
  //@ts-ignore
36
36
  const express_1 = __importDefault(require("express"));
37
37
  const http_1 = __importDefault(require("http"));
@@ -43,6 +43,7 @@ const parse_function_1 = __importDefault(require("parse-function"));
43
43
  const __1 = require("..");
44
44
  const qs_1 = __importDefault(require("qs"));
45
45
  const xmlbuilder2_1 = require("xmlbuilder2");
46
+ const chatwoot_1 = require("./integrations/chatwoot");
46
47
  exports.app = express_1.default();
47
48
  exports.server = http_1.default.createServer(exports.app);
48
49
  const trimChatId = (chatId) => chatId.replace("@c.us", "").replace("@g.us", "");
@@ -233,6 +234,11 @@ const setupTwilioCompatibleWebhook = (cliConfig, client) => {
233
234
  }));
234
235
  };
235
236
  exports.setupTwilioCompatibleWebhook = setupTwilioCompatibleWebhook;
237
+ const setupChatwoot = (cliConfig, client) => __awaiter(void 0, void 0, void 0, function* () {
238
+ exports.app.post('/chatwoot', chatwoot_1.chatwootMiddleware(cliConfig, client));
239
+ yield chatwoot_1.setupChatwootOutgoingMessageHandler(cliConfig, client);
240
+ });
241
+ exports.setupChatwoot = setupChatwoot;
236
242
  const setupBotPressHandler = (cliConfig, client) => {
237
243
  const u = cliConfig.botPressUrl;
238
244
  const sendBotPressMessage = (text, chatId, message) => __awaiter(void 0, void 0, void 0, function* () {
package/dist/cli/setup.js CHANGED
@@ -63,6 +63,16 @@ const optionList = [{
63
63
  type: String,
64
64
  typeLabel: '{blue {underline http://localhost:5555/incoming}}',
65
65
  description: "Send twillio payloads to this URL. EASY API will also parse and processes twillio response message payloads."
66
+ }, {
67
+ name: 'chatwoot-url',
68
+ type: String,
69
+ typeLabel: '{blue {underline http://localhost:3000/api/v1/accounts/3/inboxes/1}}',
70
+ description: "The URL of the specific Chatwoot inbox you set up for this session"
71
+ }, {
72
+ name: 'chatwoot-api-access-token',
73
+ type: String,
74
+ typeLabel: '{blue {underline mEEwUGEEML2ZThMm252rLg1M}}',
75
+ description: "The access token of the specific Chatwoot inbox you set up for this session"
66
76
  },
67
77
  {
68
78
  name: 'port',
@@ -48,7 +48,11 @@ const puppeteerConfig = {
48
48
  // '--no-zygote',
49
49
  // '--renderer-process-limit=1',
50
50
  // '--no-first-run'
51
- '--disable-gl-drawing-for-tests'
51
+ '--disable-gl-drawing-for-tests',
52
+ //keep awake in all situations
53
+ '--disable-background-timer-throttling',
54
+ '--disable-backgrounding-occluded-windows',
55
+ '--disable-renderer-backgrounding'
52
56
  ]
53
57
  };
54
58
  exports.puppeteerConfig = puppeteerConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-wa/wa-automate",
3
- "version": "4.25.1",
3
+ "version": "4.26.0",
4
4
  "licenseCheckUrl": "https://openwa.dev/license-check",
5
5
  "brokenMethodReportUrl": "https://openwa.dev/report-bm",
6
6
  "patches": "https://cdn.openwa.dev/patches.json",