@resultcrafter/aimanager-instagram-connector 0.1.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/LICENSE +26 -0
- package/README.md +50 -0
- package/aimanager/FacebookClient.js +223 -0
- package/aimanager/KVBaseMongo.js +99 -0
- package/aimanager/MessageHandler.js +138 -0
- package/aimanager/TiledeskAppsClient.js +158 -0
- package/aimanager/TiledeskChannel.js +257 -0
- package/aimanager/TiledeskInstagramTranslator.js +452 -0
- package/aimanager/TiledeskSubscriptionClient.js +131 -0
- package/docs/adding-new-channel-guide.md +848 -0
- package/docs/connector-research-design.md +64 -0
- package/docs/connector-research-proposal.md +23 -0
- package/docs/design.md +53 -0
- package/docs/proposal/design.md +44 -0
- package/docs/proposal/proposal.md +38 -0
- package/docs/proposal/tasks.md +38 -0
- package/docs/proposal.md +37 -0
- package/docs/specs/instagram-connector/spec.md +44 -0
- package/docs/tasks.md +46 -0
- package/index.js +963 -0
- package/package.json +32 -0
- package/publish.sh +36 -0
- package/template/configure.html +378 -0
- package/template/css/configure.css +500 -0
- package/template/css/detail.css +236 -0
- package/template/detail.html +296 -0
- package/template/error.html +64 -0
- package/test/test_translate_instagram.js +486 -0
- package/winston.js +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tiledesk SRL (original work)
|
|
4
|
+
Copyright (c) 2026 ResultCrafter (modifications)
|
|
5
|
+
|
|
6
|
+
This software is derived from @tiledesk/tiledesk-messenger-connector (MIT License)
|
|
7
|
+
and @tiledesk/tiledesk-telegram-connector (MIT License).
|
|
8
|
+
See https://github.com/Tiledesk/tiledesk-telegram-connector for original source.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# AI Manager Instagram DM Connector
|
|
2
|
+
|
|
3
|
+
> Instagram Direct Messages connector for [AI Manager](https://aimanager-app.pages.dev).
|
|
4
|
+
|
|
5
|
+
This package allows AI Manager to receive and reply to Instagram Direct Messages. Agents handle Instagram conversations from the unified dashboard alongside Telegram, WhatsApp, Messenger, and web widget channels.
|
|
6
|
+
|
|
7
|
+
## License & Attribution
|
|
8
|
+
|
|
9
|
+
**MIT License** — see [LICENSE](./LICENSE).
|
|
10
|
+
|
|
11
|
+
This software is derived from:
|
|
12
|
+
- [`@tiledesk/tiledesk-messenger-connector`](https://www.npmjs.com/package/@tiledesk/tiledesk-messenger-connector) (MIT) — base blueprint
|
|
13
|
+
- [`@tiledesk/tiledesk-telegram-connector`](https://github.com/Tiledesk/tiledesk-telegram-connector) (MIT) — architecture reference
|
|
14
|
+
|
|
15
|
+
All original Tiledesk packages are MIT licensed. This package retains the MIT license and copyright notice as required.
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
**Under development.** The repo is seeded from the Messenger connector with all references renamed. Remaining work per the [implementation plan](./docs/proposal.md):
|
|
20
|
+
|
|
21
|
+
1. Replace basic config form with Instagram OAuth flow
|
|
22
|
+
2. Adapt FacebookClient.js for Instagram Graph API
|
|
23
|
+
3. Wire up webhook handlers for Instagram DM payloads
|
|
24
|
+
4. Add Dashboard UI constants and badges
|
|
25
|
+
5. Register as AI Manager server pubmodule
|
|
26
|
+
|
|
27
|
+
See the full plan in [`docs/proposal.md`](./docs/proposal.md).
|
|
28
|
+
|
|
29
|
+
## Documentation
|
|
30
|
+
|
|
31
|
+
| Document | Description |
|
|
32
|
+
|----------|-------------|
|
|
33
|
+
| [`docs/proposal.md`](./docs/proposal.md) | Instagram connector implementation proposal |
|
|
34
|
+
| [`docs/design.md`](./docs/design.md) | Technical design decisions |
|
|
35
|
+
| [`docs/specs/`](./docs/specs/) | Requirements and scenarios |
|
|
36
|
+
| [`docs/tasks.md`](./docs/tasks.md) | Implementation tasks |
|
|
37
|
+
| [`docs/adding-new-channel-guide.md`](./docs/adding-new-channel-guide.md) | Universal guide for adding any channel to AI Manager |
|
|
38
|
+
| [`docs/connector-research-proposal.md`](./docs/connector-research-proposal.md) | Research findings on existing connectors |
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install
|
|
44
|
+
npm start
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Related
|
|
48
|
+
|
|
49
|
+
- [AI Manager](https://aimanager-app.pages.dev)
|
|
50
|
+
- [Tiledesk Telegram Connector](https://github.com/Tiledesk/tiledesk-telegram-connector) (MIT)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const axios = require("axios").default;
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const FormData = require('form-data');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const winston = require('../winston');
|
|
6
|
+
|
|
7
|
+
class FacebookClient {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor for FacebookClient
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, APP_ID: APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} config JSON configuration.
|
|
15
|
+
* @param {string} config.GRAPH_URL Mandatory. The api url for facebook.
|
|
16
|
+
* @param {string} config.APP_ID Mandatory. The facebook developer app id.
|
|
17
|
+
* @param {boolean} options.log Optional. If true HTTP requests are logged.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
constructor(config) {
|
|
21
|
+
if (!config) {
|
|
22
|
+
throw new Error('config is mandatory');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!config.GRAPH_URL) {
|
|
26
|
+
throw new Error('config.GRAPH_URL is mandatory');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!config.FB_APP_ID) {
|
|
30
|
+
throw new Error('config.APP_ID is mandatory')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!config.APP_SECRET) {
|
|
34
|
+
throw new Error('config.APP_SECRET is mandatory')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!config.BASE_URL) {
|
|
38
|
+
throw new Error('config.BASE_URL is mandatory')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.graph_url = config.GRAPH_URL;
|
|
42
|
+
this.app_id = config.FB_APP_ID;
|
|
43
|
+
this.app_secret = config.APP_SECRET;
|
|
44
|
+
this.base_url = config.BASE_URL;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async send(message, page_access_token) {
|
|
48
|
+
|
|
49
|
+
winston.debug("(fbm) [FacebookClient] Sending message...");
|
|
50
|
+
|
|
51
|
+
return await axios({
|
|
52
|
+
url: this.graph_url + "me/messages?access_token=" + page_access_token,
|
|
53
|
+
header: {
|
|
54
|
+
'Content-Type': 'application/json'
|
|
55
|
+
},
|
|
56
|
+
method: "POST",
|
|
57
|
+
data: message
|
|
58
|
+
}).then((response) => {
|
|
59
|
+
winston.debug("(fbm) [FacebookClient] Message sent!");
|
|
60
|
+
return response
|
|
61
|
+
}).catch((err) => {
|
|
62
|
+
winston.error("(fbm) [FacebookClient] error send message: ", err.response.data);
|
|
63
|
+
throw err;
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getAccessTokenFromCode(code, callback) {
|
|
68
|
+
|
|
69
|
+
const params = {
|
|
70
|
+
client_id: this.app_id,
|
|
71
|
+
client_secret: this.app_secret,
|
|
72
|
+
redirect_uri: this.base_url + '/oauth',
|
|
73
|
+
code: code
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const URL = this.graph_url + 'oauth/access_token';
|
|
77
|
+
//const URL = "https://graph.facebook.com/v9.0/oauth/access_token";
|
|
78
|
+
const HTTPREQUEST = {
|
|
79
|
+
url: URL,
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
},
|
|
83
|
+
params: params,
|
|
84
|
+
json: true,
|
|
85
|
+
method: 'GET'
|
|
86
|
+
}
|
|
87
|
+
let promise = new Promise((resolve, reject) => {
|
|
88
|
+
FacebookClient.myrequest(
|
|
89
|
+
HTTPREQUEST,
|
|
90
|
+
function(err, resbody) {
|
|
91
|
+
if (err) {
|
|
92
|
+
if (callback) {
|
|
93
|
+
callback(err);
|
|
94
|
+
}
|
|
95
|
+
reject(err);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
if (callback) {
|
|
99
|
+
callback(null, resbody);
|
|
100
|
+
}
|
|
101
|
+
resolve(resbody.access_token);
|
|
102
|
+
}
|
|
103
|
+
}, true)
|
|
104
|
+
})
|
|
105
|
+
return promise;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getPages(access_token, callback) {
|
|
109
|
+
|
|
110
|
+
const params = {
|
|
111
|
+
access_token: access_token
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const URL = this.graph_url + 'me/accounts';
|
|
115
|
+
const HTTPREQUEST = {
|
|
116
|
+
url: URL,
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
},
|
|
120
|
+
params: params,
|
|
121
|
+
method: 'GET'
|
|
122
|
+
}
|
|
123
|
+
let promise = new Promise((resolve, reject) => {
|
|
124
|
+
FacebookClient.myrequest(
|
|
125
|
+
HTTPREQUEST,
|
|
126
|
+
function(err, resbody) {
|
|
127
|
+
if (err) {
|
|
128
|
+
if (callback) {
|
|
129
|
+
callback(err);
|
|
130
|
+
}
|
|
131
|
+
reject(err);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
if (callback) {
|
|
135
|
+
callback(null, resbody);
|
|
136
|
+
}
|
|
137
|
+
resolve(resbody.data);
|
|
138
|
+
}
|
|
139
|
+
}, true)
|
|
140
|
+
})
|
|
141
|
+
return promise;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async messageEventSubscription(page_id, access_token) {
|
|
145
|
+
|
|
146
|
+
return await axios({
|
|
147
|
+
url: this.graph_url + page_id + "/subscribed_apps?access_token=" + access_token + "&subscribed_fields=['messages', 'messaging_postbacks']",
|
|
148
|
+
method: "POST"
|
|
149
|
+
}).then((response) => {
|
|
150
|
+
winston.debug("(fbm) [FacebookClient] event subscription resbody: ", response);
|
|
151
|
+
return response
|
|
152
|
+
}).catch((err) => {
|
|
153
|
+
winston.error("(fbm) [FacebookClient] event subscription error: ", err)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
/*
|
|
157
|
+
let promise = new Promise((resolve, reject) => {
|
|
158
|
+
|
|
159
|
+
request({
|
|
160
|
+
url: `${this.graph_url}/${page_id}/subscribed_apps?access_token=${access_token}&subscribed_fields=messages`,
|
|
161
|
+
method: 'POST'
|
|
162
|
+
}, (err, res, resbody) => {
|
|
163
|
+
if (err) {
|
|
164
|
+
reject(err)
|
|
165
|
+
} else {
|
|
166
|
+
resolve(resbody)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
return promise;
|
|
171
|
+
*/
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async getUserInfo(access_token, user_id) {
|
|
175
|
+
try {
|
|
176
|
+
const response = await axios({
|
|
177
|
+
url: this.graph_url + user_id + "?access_token=" + access_token,
|
|
178
|
+
method: 'GET'
|
|
179
|
+
});
|
|
180
|
+
winston.debug("(fbm) [FacebookClient] get user info response: ", response.data);
|
|
181
|
+
return response.data;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
winston.error("(fbm) [FacebookClient] get user info err: ", err.response?.data || err.message);
|
|
184
|
+
// Return default user info to prevent undefined errors
|
|
185
|
+
return {
|
|
186
|
+
first_name: "User",
|
|
187
|
+
last_name: user_id
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// HTTP REQUEST
|
|
193
|
+
|
|
194
|
+
static async myrequest(options, callback, log) {
|
|
195
|
+
|
|
196
|
+
return await axios({
|
|
197
|
+
url: options.url,
|
|
198
|
+
method: options.method,
|
|
199
|
+
data: options.json,
|
|
200
|
+
params: options.params,
|
|
201
|
+
headers: options.headers
|
|
202
|
+
}).then((res) => {
|
|
203
|
+
if (res && res.status == 200 && res.data) {
|
|
204
|
+
if (callback) {
|
|
205
|
+
callback(null, res.data);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
if (callback) {
|
|
210
|
+
callback({ message: "Response status not 200", data: res.data }, null, null);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}).catch((err) => {
|
|
214
|
+
winston.error("(fbm) [FacebookClient] An error occured: ", err);
|
|
215
|
+
if (callback) {
|
|
216
|
+
callback(err, null, null);
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = { FacebookClient }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const mongodb = require("mongodb");
|
|
2
|
+
const winston = require('../winston');
|
|
3
|
+
|
|
4
|
+
class KVBaseMongo {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Constructor for KVBaseMongo object
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { KVBaseMongo } = require('./KVBaseMongo');
|
|
11
|
+
* let db = new KVBaseMongo("kvstore");
|
|
12
|
+
*
|
|
13
|
+
* @param {KVBASE_COLLECTION} The name of the Mongodb collection used as key-value store. Mandatory.
|
|
14
|
+
*/
|
|
15
|
+
constructor(KVBASE_COLLECTION) {
|
|
16
|
+
if (!KVBASE_COLLECTION) {
|
|
17
|
+
throw new Error('KVBASE_COLLECTION (the name of the Mongodb collection used as key-value store) is mandatory.');
|
|
18
|
+
}
|
|
19
|
+
this.KV_COLLECTION = KVBASE_COLLECTION;
|
|
20
|
+
winston.verbose("KV_COLLECTION: " + this.KV_COLLECTION)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
connect(MONGODB_URI, callback) {
|
|
24
|
+
mongodb.MongoClient.connect(MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }, (err, client) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
winston.log(err);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
} else {
|
|
29
|
+
this.db = client.db();
|
|
30
|
+
this.db.collection(this.KV_COLLECTION).createIndex(
|
|
31
|
+
{ "key": 1 }, { unique: true }
|
|
32
|
+
);
|
|
33
|
+
callback();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
reuseConnection(db, callback) {
|
|
39
|
+
this.db = db;
|
|
40
|
+
this.db.collection(this.KV_COLLECTION).createIndex(
|
|
41
|
+
{ "key": 1 }, { unique: true }
|
|
42
|
+
)
|
|
43
|
+
callback();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
set(k, v) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
//this.db.set(k, v).then(() => {resolve();});
|
|
49
|
+
this.db.collection(this.KV_COLLECTION).updateOne({key: k}, { $set: { value: v, key: k } }, { upsert: true }, function(err, doc) {
|
|
50
|
+
if (err) {
|
|
51
|
+
reject(err);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
resolve();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get(k) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
//this.db.get(k).then(value => {resolve(value)});
|
|
63
|
+
winston.debug("Searching on " + this.db)
|
|
64
|
+
winston.verbose("Searching on Collection" + this.KV_COLLECTION)
|
|
65
|
+
|
|
66
|
+
this.db.collection(this.KV_COLLECTION).findOne({ key: k }, function(err, doc) {
|
|
67
|
+
if (err) {
|
|
68
|
+
winston.error("Error reading mongodb value" + err);
|
|
69
|
+
reject(err);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (doc) {
|
|
73
|
+
winston.verbose("Doc found with key: " + doc.key);
|
|
74
|
+
resolve(doc.value);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
winston.verbose("No Doc found!");
|
|
78
|
+
resolve(null);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
remove(k) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
this.db.collection(this.KV_COLLECTION).deleteOne({key: k}, function(err) {
|
|
88
|
+
if (err) {
|
|
89
|
+
reject(err);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
resolve();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { KVBaseMongo };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const axios = require("axios").default;
|
|
2
|
+
const jwt = require('jsonwebtoken');
|
|
3
|
+
const { v4: uuidv4 } = require('uuid');
|
|
4
|
+
const winston = require('../winston');
|
|
5
|
+
|
|
6
|
+
class MessageHandler {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Constructor for TiledeskChannel
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const { TiledeskChannel } = require('tiledesk-channel');
|
|
13
|
+
* const tdChannel = new TiledeskChannel({tiledeskJsonMessage: replyFromWhatsapp, settings: appSettings, whatsappJsonMessage: originalWhatsappMessage, API_URL: tiledeskApiUrl });
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} config JSON configuration.
|
|
16
|
+
* @param {string} config.tiledeskJsonMessage Mandatory. Message translated from Whatsapp to Tiledesk
|
|
17
|
+
* @param {string} config.whatsappJsonMessage Mandatory. Original whatsapp message.
|
|
18
|
+
* @param {string} config.settings Mandatory. Installation settings.
|
|
19
|
+
* @param {string} config.API_URL Mandatory. Tiledesk api url.
|
|
20
|
+
* @param {boolean} options.log Optional. If true HTTP requests are logged.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
constructor(config) {
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
if (!config) {
|
|
27
|
+
throw new Error('config is mandatory');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!config.tiledeskChannelMessage) {
|
|
31
|
+
throw new Error('config.tiledeskChannelMessage is mandatory');
|
|
32
|
+
}
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
if (config) {
|
|
36
|
+
if (config.tiledeskChannelMessage) {
|
|
37
|
+
this.tiledeskChannelMessage = config.tiledeskChannelMessage;
|
|
38
|
+
} else {
|
|
39
|
+
winston.verbose("Missing config.tiledeskChannelMessage")
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.log = false;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async generateMessageObject(command) {
|
|
48
|
+
winston.debug("(fbm) [MessageHandler] command: ", command);
|
|
49
|
+
|
|
50
|
+
let tiledeskCommandMessage = command.message;
|
|
51
|
+
tiledeskCommandMessage.recipient = this.tiledeskChannelMessage.recipient;
|
|
52
|
+
|
|
53
|
+
return tiledeskCommandMessage;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async splitMessageFromTiledesk(tiledeskMessage) {
|
|
57
|
+
|
|
58
|
+
winston.debug("(fbm) [MessageHandler] split message tiledeskMessage: ", tiledeskMessage);
|
|
59
|
+
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
|
|
62
|
+
let messagesList = [];
|
|
63
|
+
|
|
64
|
+
if (tiledeskMessage.metadata) {
|
|
65
|
+
let message = Object.assign({}, tiledeskMessage);
|
|
66
|
+
delete message.text;
|
|
67
|
+
|
|
68
|
+
messagesList.push(message)
|
|
69
|
+
|
|
70
|
+
if (tiledeskMessage.text != null && tiledeskMessage.text != "") {
|
|
71
|
+
message = Object.assign({}, tiledeskMessage);
|
|
72
|
+
delete message.metadata;
|
|
73
|
+
message.type = "text";
|
|
74
|
+
|
|
75
|
+
messagesList.push(message);
|
|
76
|
+
resolve(messagesList);
|
|
77
|
+
} else {
|
|
78
|
+
resolve(messagesList);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
messagesList.push(tiledeskMessage);
|
|
82
|
+
resolve(messagesList);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async splitMessageFromInstagram(instagramMessage) {
|
|
89
|
+
|
|
90
|
+
winston.debug("(fbm) [MessageHandler] split message instagramMessage: ", instagramMessage);
|
|
91
|
+
|
|
92
|
+
let messagesList = [];
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
|
|
95
|
+
let attachments = instagramMessage.message.attachments;
|
|
96
|
+
attachments.forEach((attachment) => {
|
|
97
|
+
let message = JSON.parse(JSON.stringify(instagramMessage));
|
|
98
|
+
delete message.message.attachments;
|
|
99
|
+
message.message.attachments = [ attachment ];
|
|
100
|
+
|
|
101
|
+
messagesList.push(message);
|
|
102
|
+
})
|
|
103
|
+
resolve(messagesList);
|
|
104
|
+
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
generateMessageObjectOriginal(command_message) {
|
|
111
|
+
let parentUid = this.tiledeskChannelMessage.uid
|
|
112
|
+
//command_message.uid = this.tiledeskChannelMessage.uid + "_" + index;
|
|
113
|
+
command_message.uid = this.tiledeskChannelMessage.uid;
|
|
114
|
+
if(command_message.text) command_message.text = command_message.text.trim()//remove black msg with only spaces
|
|
115
|
+
command_message.language = message.language;
|
|
116
|
+
command_message.recipient = message.recipient;
|
|
117
|
+
command_message.recipient_fullname = message.recipient_fullname;
|
|
118
|
+
command_message.sender = message.sender;
|
|
119
|
+
command_message.sender_fullname = message.sender_fullname;
|
|
120
|
+
command_message.channel_type = message.channel_type;
|
|
121
|
+
command_message.status = message.status;
|
|
122
|
+
command_message.isSender = message.isSender;
|
|
123
|
+
command_message.attributes? command_message.attributes.commands = true : command_message.attributes = {commands : true}
|
|
124
|
+
command_message.attributes.parentUid = parentUid //added to manage message STATUS UPDATES
|
|
125
|
+
command_message.attributes = {...message.attributes, ...command_message.attributes}
|
|
126
|
+
//this.addedNew(command_message)
|
|
127
|
+
//callback();
|
|
128
|
+
|
|
129
|
+
return command_message
|
|
130
|
+
}
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { MessageHandler }
|
|
138
|
+
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const axios = require("axios").default;
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const FormData = require('form-data');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const winston = require('../winston');
|
|
6
|
+
|
|
7
|
+
class TiledeskAppsClient {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor for TiledeskChannel
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const { TiledeskAppsClient } = require('tiledesk-apps-client');
|
|
13
|
+
* const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL});
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} config JSON configuration.
|
|
16
|
+
* @param {string} config.APPS_API_URL Mandatory. The api url for tiledesk apps.
|
|
17
|
+
* @param {boolean} options.log Optional. If true HTTP requests are logged.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
constructor(config) {
|
|
21
|
+
if (!config) {
|
|
22
|
+
throw new Error('config is mandatory');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!config.APPS_API_URL) {
|
|
26
|
+
throw new Error('config.APPS_URL is mandatory');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.APPS_API_URL = config.APPS_API_URL;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
install(installation_info, callback) {
|
|
34
|
+
|
|
35
|
+
const URL = this.APPS_API_URL + `/api/installation`;
|
|
36
|
+
const HTTPREQUEST = {
|
|
37
|
+
url: URL,
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
},
|
|
41
|
+
json: installation_info,
|
|
42
|
+
method: 'POST'
|
|
43
|
+
};
|
|
44
|
+
let promise = new Promise((resolve, reject) => {
|
|
45
|
+
TiledeskAppsClient.myrequest(
|
|
46
|
+
HTTPREQUEST,
|
|
47
|
+
function(err, resbody) {
|
|
48
|
+
if (err) {
|
|
49
|
+
if (callback) {
|
|
50
|
+
callback(err);
|
|
51
|
+
}
|
|
52
|
+
reject(err);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
if (callback) {
|
|
56
|
+
callback(null, resbody);
|
|
57
|
+
}
|
|
58
|
+
winston.verbose("(fbm) [TiledeskAppsClient] Installed!");
|
|
59
|
+
resolve(resbody);
|
|
60
|
+
}
|
|
61
|
+
}, true);
|
|
62
|
+
})
|
|
63
|
+
return promise;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getInstallations(project_id, app_id) {
|
|
67
|
+
const URL = this.APPS_API_URL + `/api/installation/${project_id}`;
|
|
68
|
+
const HTTPREQUEST = {
|
|
69
|
+
url: URL,
|
|
70
|
+
headers: {
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
},
|
|
73
|
+
method: 'GET'
|
|
74
|
+
};
|
|
75
|
+
let promise = new Promise((resolve, reject) => {
|
|
76
|
+
TiledeskAppsClient.myrequest(
|
|
77
|
+
HTTPREQUEST,
|
|
78
|
+
function(err, resbody) {
|
|
79
|
+
if (err) {
|
|
80
|
+
reject(err);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
let obj = resbody.find(o => o.app_id === app_id);
|
|
84
|
+
if (obj) {
|
|
85
|
+
resolve(obj);
|
|
86
|
+
} else {
|
|
87
|
+
resolve(null);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, true);
|
|
91
|
+
})
|
|
92
|
+
return promise;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
uninstall(project_id, app_id, callback) {
|
|
96
|
+
const URL = this.APPS_API_URL + `/api/installation/${project_id}/${app_id}`;
|
|
97
|
+
const HTTPREQUEST = {
|
|
98
|
+
url: URL,
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
},
|
|
102
|
+
method: 'DELETE'
|
|
103
|
+
};
|
|
104
|
+
let promise = new Promise((resolve, reject) => {
|
|
105
|
+
TiledeskAppsClient.myrequest(
|
|
106
|
+
HTTPREQUEST,
|
|
107
|
+
function(err, resbody) {
|
|
108
|
+
if (err) {
|
|
109
|
+
if (callback) {
|
|
110
|
+
callback(err);
|
|
111
|
+
}
|
|
112
|
+
reject(err);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
if (callback) {
|
|
116
|
+
callback(null, resbody);
|
|
117
|
+
}
|
|
118
|
+
winston.verbose("(fbm) [TiledeskAppsClient] Uninstalled!");
|
|
119
|
+
resolve(resbody);
|
|
120
|
+
}
|
|
121
|
+
}, true);
|
|
122
|
+
})
|
|
123
|
+
return promise;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
// HTTP REQUEST
|
|
129
|
+
|
|
130
|
+
static async myrequest(options, callback, log) {
|
|
131
|
+
return await axios({
|
|
132
|
+
url: options.url,
|
|
133
|
+
method: options.method,
|
|
134
|
+
data: options.json,
|
|
135
|
+
params: options.params,
|
|
136
|
+
headers: options.headers
|
|
137
|
+
}).then((res) => {
|
|
138
|
+
if (res && res.status == 200 && res.data) {
|
|
139
|
+
if (callback) {
|
|
140
|
+
callback(null, res.data);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
if (callback) {
|
|
145
|
+
callback(TiledeskClient.getErr({ message: "Response status not 200" }, options, res), null, null);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}).catch((err) => {
|
|
149
|
+
winston.error("(fbm) [TiledeskAppsClient] An error occured: ", err);
|
|
150
|
+
if (callback) {
|
|
151
|
+
callback(err, null, null);
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { TiledeskAppsClient }
|