@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 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 }