@tiledesk/tiledesk-tybot-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/index.js ADDED
@@ -0,0 +1,376 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const bodyParser = require('body-parser');
4
+ var cors = require('cors');
5
+
6
+ //router.use(cors());
7
+ router.use(bodyParser.json({limit: '50mb'}));
8
+ router.use(bodyParser.urlencoded({ extended: true , limit: '50mb'}));
9
+
10
+ // DEV
11
+ //const { MessagePipeline } = require('./tiledeskChatbotPlugs/MessagePipeline');
12
+ //const { DirectivesChatbotPlug } = require('./tiledeskChatbotPlugs/DirectivesChatbotPlug');
13
+ //const { SplitsChatbotPlug } = require('./tiledeskChatbotPlugs/SplitsChatbotPlug');
14
+ //const { MarkbotChatbotPlug } = require('./tiledeskChatbotPlugs/MarkbotChatbotPlug');
15
+ //const { WebhookChatbotPlug } = require('./tiledeskChatbotPlugs/WebhookChatbotPlug');
16
+
17
+ // PROD
18
+ const { MessagePipeline } = require('@tiledesk/tiledesk-chatbot-plugs/MessagePipeline');
19
+ const { DirectivesChatbotPlug } = require('@tiledesk/tiledesk-chatbot-plugs/DirectivesChatbotPlug');
20
+ const { SplitsChatbotPlug } = require('@tiledesk/tiledesk-chatbot-plugs/SplitsChatbotPlug');
21
+ const { MarkbotChatbotPlug } = require('@tiledesk/tiledesk-chatbot-plugs/MarkbotChatbotPlug');
22
+ const { WebhookChatbotPlug } = require('@tiledesk/tiledesk-chatbot-plugs/WebhookChatbotPlug');
23
+
24
+ var path = require("path");
25
+ var fs = require('fs');
26
+
27
+ const { TiledeskChatbotClient } = require('@tiledesk/tiledesk-chatbot-client');
28
+ const jwt = require('jsonwebtoken');
29
+ const { v4: uuidv4 } = require('uuid');
30
+
31
+ // THE IMPORT
32
+ var mongoose = require('mongoose');
33
+ var Faq = require('./models/faq');
34
+ var Faq_kb = require('./models/faq_kb');
35
+ let connection;
36
+
37
+ router.post('/ext/:botid', async (req, res) => {
38
+ console.log("Reques body:", req.body);
39
+ res.status(200).send({"success":true});
40
+
41
+ const botId = req.params.botid;
42
+ console.log("query botId:", botId);
43
+ const message = req.body.payload;
44
+ const faq_kb = req.body.hook;
45
+ const token = req.body.token;
46
+
47
+ //const bot = await Faq_kb.findById(botId).exec();
48
+ const bot = await Faq_kb.findById(botId).select('+secret').exec();
49
+ console.log("bot:", bot);
50
+
51
+ // CREATE TOKEN
52
+ //var botWithSecret = await Faq_kb.findById(bot._id).select('+secret').exec();
53
+
54
+ var signOptions = {
55
+ issuer: 'https://tiledesk.com',
56
+ subject: 'bot',
57
+ audience: 'https://tiledesk.com/bots/'+bot._id,
58
+ jwtid: uuidv4()
59
+ };
60
+ const bot_token = jwt.sign(bot.toObject(), bot.secret, signOptions);
61
+ console.log("bot_token:", bot_token);
62
+ //
63
+
64
+ // SETUP EXACT MATCH
65
+ let query = { "id_project": message.id_project, "id_faq_kb": botId, "question": message.text };
66
+ // BUT CHECKING ACTION BUTTON...
67
+ if (message.attributes && message.attributes.action) {
68
+ var action = message.attributes.action;
69
+ var action_parameters_index = action.indexOf("?");
70
+ if (action_parameters_index > -1) {
71
+ action = action.substring(0, action_parameters_index);
72
+ }
73
+ console.debug("action: " + action);
74
+ query = { "id_project": message.id_project, "id_faq_kb": botId, "intent_display_name": action };
75
+ //var isObjectId = mongoose.Types.ObjectId.isValid(action);
76
+ //console.debug("isObjectId:" + isObjectId);
77
+ //if (isObjectId) {
78
+ // query = { "id_project": message.id_project, "id_faq_kb": botId, "_id": action };
79
+ //} else {
80
+ // query = { "id_project": message.id_project, "id_faq_kb": botId, $or: [{ "intent_id": action }, { "intent_display_name": action }] };
81
+ //}
82
+ }
83
+
84
+ // SEARCH INTENTS
85
+ Faq.find(query).lean().exec(async (err, faqs) => {
86
+ if (err) {
87
+ return console.error("Error getting faq object.", err);
88
+ }
89
+ if (faqs && faqs.length > 0 && faqs[0].answer) { // EXACT MATCH!
90
+ console.log("FAQ:", faqs[0]);
91
+ execFaq(req, res, faqs, botId, message, bot_token, bot);
92
+ }
93
+ else { // FULL TEXT
94
+ console.log("Go fulltext...");
95
+ query = { "id_project": message.id_project, "id_faq_kb": botId };
96
+ var mongoproject = undefined;
97
+ var sort = undefined;
98
+ var search_obj = { "$search": message.text };
99
+
100
+ if (faq_kb.language) {
101
+ search_obj["$language"] = faq_kb.language;
102
+ }
103
+ query.$text = search_obj;
104
+ console.debug("fulltext search query", query);
105
+
106
+ mongoproject = { score: { $meta: "textScore" } };
107
+ sort = { score: { $meta: "textScore" } }
108
+ // DA QUI RECUPERO LA RISPOSTA DATO (ID: SE EXT_AI) (QUERY FULLTEXT SE NATIVE-BASIC-AI)
109
+ Faq.find(query, mongoproject).sort(sort).lean().exec(async (err, faqs) => {
110
+ console.log("Found:", faqs);
111
+ if (err) {
112
+ console.erro("Error:", err);
113
+ return console.error('Error getting fulltext objects.', err);
114
+ }
115
+ console.debug("faqs:", faqs);
116
+ if (faqs && faqs.length > 0 && faqs[0].answer) {
117
+ execFaq(req, res, faqs, botId, message, bot_token, bot);
118
+ }
119
+ else {
120
+ // fallback
121
+ const fallbackIntent = await getIntentByDisplayName("defaultFallback", bot);
122
+ const faqs = [fallbackIntent];
123
+ execFaq(req, res, faqs, botId, message, bot_token, bot);
124
+ }
125
+ });
126
+ }
127
+ });
128
+
129
+ });
130
+
131
+ async function execFaq(req, res, faqs, botId, message, token, bot) {
132
+ let sender = 'bot_' + botId;
133
+ console.debug("sender", sender);
134
+ var answerObj;
135
+ answerObj = faqs[0];
136
+ answerObj.score = 100; //exact search not set score
137
+ console.debug("answerObj.score", answerObj.score);
138
+
139
+ const context = {
140
+ payload: {
141
+ //text: text,
142
+ bot: bot,
143
+ message: message, // USER MESSAGE (JSON)
144
+ intent: answerObj
145
+ },
146
+ token: token
147
+ };
148
+ const static_bot_answer = { // static design of the chatbot reply
149
+ //type: answerObj.type,
150
+ text: answerObj.answer,
151
+ attributes: answerObj.attributes,
152
+ metadata: answerObj.metadata,
153
+ // language: ?
154
+ // channel: ? whatsapp|telegram|facebook...
155
+ };
156
+ if (!static_bot_answer.attributes) {
157
+ static_bot_answer.attributes = {}
158
+ }
159
+ static_bot_answer.attributes.directives = true;
160
+ static_bot_answer.attributes.splits = false;
161
+ static_bot_answer.attributes.markbot = true;
162
+
163
+ static_bot_answer.attributes.webhook = answerObj.webhook_enabled;
164
+ console.log("static_bot_answer.attributes", static_bot_answer.attributes)
165
+
166
+ // faq[0] => PIPELINE => bot_answer
167
+ const APIURL = "https://tiledesk-server-pre.herokuapp.com"
168
+ const messagePipeline = new MessagePipeline(static_bot_answer, context);
169
+ const webhookurl = bot.webhook_url;
170
+ messagePipeline.addPlug(new WebhookChatbotPlug(message.request, webhookurl, token));
171
+ let directivesPlug = new DirectivesChatbotPlug(message.request, APIURL, token);
172
+ messagePipeline.addPlug(directivesPlug);
173
+ messagePipeline.addPlug(new SplitsChatbotPlug());
174
+ messagePipeline.addPlug(new MarkbotChatbotPlug());
175
+ const bot_answer = await messagePipeline.exec();
176
+ console.log("End pipeline, bot_answer:", JSON.stringify(bot_answer));
177
+
178
+ var attr = bot_answer.attributes;
179
+ if (!attr) {
180
+ attr = {};
181
+ }
182
+ var timestamp = Date.now();
183
+ attr['clienttimestamp'] = timestamp;
184
+ if (answerObj && answerObj._id) {
185
+ attr._answerid = answerObj._id.toString();
186
+ }
187
+ // DECORATES THE FINAL ANSWER
188
+ // question_payload = clone of user's original message
189
+ let question_payload = Object.assign({}, message);
190
+ delete question_payload.request;
191
+ let clonedfaqs = faqs.slice();
192
+ if (clonedfaqs && clonedfaqs.length > 0) {
193
+ clonedfaqs = clonedfaqs.shift()
194
+ }
195
+ const intent_info = {
196
+ intent_name: answerObj.intent_display_name,
197
+ is_fallback: false,
198
+ confidence: answerObj.score,
199
+ question_payload: question_payload,
200
+ others: clonedfaqs
201
+ }
202
+ console.debug("intent_info", intent_info);
203
+ attr.intent_info = intent_info;
204
+ const tdclient = new TiledeskChatbotClient(
205
+ {
206
+ request: req,
207
+ APIKEY: '__APIKEY__',
208
+ APIURL: APIURL
209
+ });
210
+ console.log("Sending back:", JSON.stringify(bot_answer));
211
+ tdclient.sendMessage(bot_answer, () => {
212
+ console.log("Message sent.");
213
+ directivesPlug.processDirectives(() => {
214
+ console.log("End processing directives.");
215
+ })
216
+ });
217
+ }
218
+
219
+ function getIntentByDisplayName(name, bot) {
220
+ return new Promise(function(resolve, reject) {
221
+ var query = { "id_project": bot.id_project, "id_faq_kb": bot._id, "intent_display_name": name};
222
+ console.debug('query', query);
223
+ Faq.find(query).lean().exec(function (err, faqs) {
224
+ if (err) {
225
+ return reject();
226
+ }
227
+ console.debug("faqs", faqs);
228
+ if (faqs && faqs.length > 0) {
229
+ const intent = faqs[0];
230
+ return resolve(intent);
231
+ }
232
+ else {
233
+ return resolve(null);
234
+ }
235
+ });
236
+ });
237
+ }
238
+
239
+ // IGNORALA
240
+ /*app.post('/extorig', (req, res) => {
241
+ res.status(200).send({"success":true});
242
+ const tdclient =
243
+ new TiledeskChatbotClient(
244
+ {
245
+ APIURL: apiurl(),
246
+ request: req,
247
+ APIKEY: '__APIKEY__'
248
+ });
249
+ if (tdclient.text === "\\start") {
250
+ tdclient.sendMessage(
251
+ {
252
+ text: 'started',
253
+ attributes: {
254
+ attachment: {
255
+ type:"template",
256
+ buttons: [
257
+ {
258
+ type: "text",
259
+ value: "Ok"
260
+ }
261
+ ]
262
+ }
263
+ }
264
+ }
265
+ );
266
+ }
267
+ else {
268
+ tdclient.sendMessage({text: 'Not trained for this'});
269
+ }
270
+ });
271
+ */
272
+
273
+ // Tiledesk Resolution-bot webhook endpoint
274
+ /*app.post('/bot', async (req, res) => {
275
+ console.log("Webhook. Request body: " + JSON.stringify(req.body));
276
+ // INTENTS
277
+ let intent = null;
278
+ intent = req.body.payload.intent.intent_display_name;
279
+ //const projectId = req.body.payload.bot.id_project;
280
+ const token = req.body.token;
281
+ const request = req.body.payload.message.request;
282
+ //const requestId = request.request_id;
283
+ console.log("Got intent:", intent);
284
+ API_URL = apiurl();
285
+ const original_answer = req.body.payload.intent.answer;
286
+
287
+ const messagePipeline = new MessagePipeline();
288
+ messagePipeline.addPlug(new DirectivesChatbotPlug(request, API_URL, token));
289
+ messagePipeline.addPlug(new SplitsChatbotPlug());
290
+ messagePipeline.addPlug(new MarkbotChatbotPlug());
291
+
292
+ const message = {
293
+ text: original_answer
294
+ };
295
+ messagePipeline.execOn(message, (_message) => {
296
+ res.json(_message);
297
+ console.log("End pipeline.");
298
+ });
299
+
300
+ });*/
301
+
302
+
303
+
304
+
305
+
306
+ function apiurl() {
307
+ const server = "pre";
308
+ //const server = "prod";
309
+ const API_URL_PRE = 'https://tiledesk-server-pre.herokuapp.com';
310
+ const API_URL_PROD = 'https://api.tiledesk.com/v2';
311
+ // choose a server
312
+ let API_URL = API_URL_PROD;
313
+ if (server === 'pre') {
314
+ API_URL = API_URL_PRE;
315
+ }
316
+ return API_URL;
317
+ }
318
+
319
+
320
+ router.get('/', (req, res) => {
321
+ res.send('Hello Tybot!');
322
+ });
323
+
324
+ function startTybot(settings, completionCallback) {
325
+ console.log("Starting Tybot with Settings:", settings);
326
+
327
+ if (!settings.MONGODB_URI) {
328
+ throw new Error("settings.MONGODB_URI is mandatory.");
329
+ }
330
+ if (!settings.API_ENDPOINT) {
331
+ throw new Error("settings.API_ENDPOINT is mandatory.");
332
+ }
333
+ else {
334
+ API_ENDPOINT = settings.API_ENDPOINT;
335
+ console.log("(Tybot) settings.API_ENDPOINT:", API_ENDPOINT);
336
+ }
337
+ if (!settings.log) {
338
+ log = false;
339
+ }
340
+
341
+ console.log("Starting Tybot connector...");
342
+ console.log("(Tybot) Connecting to mongodb...");
343
+
344
+ connection = mongoose.connect(process.env.mongoUrl, { "useNewUrlParser": true, "autoIndex": false }, function(err) {
345
+ if (err) {
346
+ console.error('Failed to connect to MongoDB on ' + settings.MONGODB_URI + " ", err);
347
+ //process.exit(1); // add => exitOnFail: true
348
+ }
349
+ else {
350
+ console.info("Tybot Mongodb connected.");
351
+ if (completionCallback) {
352
+ completionCallback();
353
+ }
354
+ }
355
+ });
356
+ }
357
+
358
+ /*
359
+ var connection = mongoose.connect(process.env.mongoUrl, { "useNewUrlParser": true, "autoIndex": false }, function(err) {
360
+ if (err) {
361
+ console.error('Failed to connect to MongoDB on ' + process.env.mongoUrl + " ", err);
362
+ //process.exit(1);
363
+ }
364
+ else {
365
+ console.info("Mongodb connected")
366
+ }
367
+ });
368
+ */
369
+
370
+ /*
371
+ app.listen(3000, () => {
372
+ console.log('server started');
373
+ });
374
+ */
375
+
376
+ module.exports = { router: router, startTybot: startTybot};
package/models/faq.js ADDED
@@ -0,0 +1,120 @@
1
+ var mongoose = require('mongoose');
2
+ var Schema = mongoose.Schema;
3
+ var { nanoid } = require("nanoid");
4
+ const uuidv4 = require('uuid/v4');
5
+
6
+ var defaultFullTextLanguage = process.env.DEFAULT_FULLTEXT_INDEX_LANGUAGE || "none";
7
+
8
+ var FaqSchema = new Schema({
9
+ id_faq_kb: {
10
+ type: String,
11
+ index: true
12
+ },
13
+ intent_id: {
14
+ type: String,
15
+ required: false,
16
+ index:true,
17
+ default: function() {
18
+ return uuidv4();
19
+ }
20
+ },
21
+ intent_display_name: { //documentare
22
+ type: String,
23
+ required: false,
24
+ index:true,
25
+ default: function() {
26
+ return nanoid(6);
27
+ }
28
+ },
29
+ question: {
30
+ type: String,
31
+ required: true
32
+ },
33
+
34
+ webhook_enabled: { //usa questo
35
+ type: Boolean,
36
+ required: false,
37
+ default: false,
38
+ },
39
+ answer: {
40
+ type: String,
41
+ required: true
42
+ },
43
+ id_project: {
44
+ type: String,
45
+ required: true,
46
+ index: true
47
+ },
48
+ topic: {
49
+ type: String,
50
+ default: "default",
51
+ index: true
52
+ // required: true
53
+ },
54
+ status: {
55
+ type: String,
56
+ default: "live",
57
+ index: true
58
+ // required: true
59
+ },
60
+ language: {
61
+ type: String,
62
+ required: false,
63
+ index: true
64
+ },
65
+
66
+ // "stats":{
67
+ // "conversation_count":2,
68
+ // "all_done_count":0,
69
+ // "wait_for_team_count":2
70
+ // }
71
+
72
+ createdBy: {
73
+ type: String,
74
+ required: true
75
+ }
76
+ },{
77
+ timestamps: true,
78
+ toJSON: { virtuals: true } //used to polulate messages in toJSON// https://mongoosejs.com/docs/populate.html
79
+ }
80
+ );
81
+
82
+ FaqSchema.virtual('faq_kb', {
83
+ ref: 'faq_kb', // The model to use
84
+ localField: 'id_faq_kb', // Find people where `localField`
85
+ foreignField: '_id', // is equal to `foreignField`
86
+ justOne: false,
87
+ //options: { sort: { name: -1 }, limit: 5 } // Query options, see http://bit.ly/mongoose-query-options
88
+ });
89
+
90
+ FaqSchema.index({ id_project: 1, id_faq_kb: 1, question: 1 });
91
+
92
+ // https://docs.mongodb.com/manual/core/index-text/
93
+ // https://docs.mongodb.com/manual/tutorial/specify-language-for-text-index/
94
+ // https://docs.mongodb.com/manual/reference/text-search-languages/#text-search-languages
95
+
96
+
97
+ FaqSchema.index({question: 'text'},
98
+ {"name":"faq_fulltext","default_language": defaultFullTextLanguage,"language_override": "language"}); // schema level
99
+
100
+ // FaqSchema.index({question: 'text', answer: 'text'},
101
+ // {"name":"faq_fulltext","default_language": defaultFullTextLanguage,"language_override": "language", weights: {question: 10,answer: 1}}); // schema level
102
+
103
+
104
+ FaqSchema.index({ id_project: 1, id_faq_kb: 1, intent_display_name: 1 }, { unique: true });
105
+
106
+
107
+ var faq = mongoose.model('faq', FaqSchema);
108
+
109
+ //faq.on('index', function(error) {
110
+ // "_id index cannot be sparse"
111
+ //console.debug('index:', error);
112
+ //});
113
+
114
+ if (process.env.MONGOOSE_SYNCINDEX) {
115
+ faq.syncIndexes();
116
+ console.info("faq syncIndexes")
117
+ }
118
+
119
+
120
+ module.exports = faq;
@@ -0,0 +1,87 @@
1
+ var mongoose = require('mongoose');
2
+ var Schema = mongoose.Schema;
3
+ const uuidv4 = require('uuid/v4');
4
+
5
+ var Faq_kbSchema = new Schema({
6
+ name: {
7
+ type: String,
8
+ required: true,
9
+ index:true
10
+ },
11
+ description: {
12
+ type: String,
13
+ // index:true
14
+ },
15
+ url: {
16
+ type: String,
17
+ // required: true
18
+ },
19
+ webhook_url: {
20
+ type: String,
21
+ // required: true
22
+ },
23
+ webhook_enabled: {
24
+ type: Boolean,
25
+ required: false,
26
+ default: false,
27
+ },
28
+ id_project: {
29
+ type: String,
30
+ required: true,
31
+ index: true
32
+ },
33
+ // kbkey_remote: { //serve?
34
+ // type: String,
35
+ // },
36
+ type: {
37
+ type: String,
38
+ default: 'internal',
39
+ index: true
40
+ },
41
+ // external: {
42
+ // type: Boolean,
43
+ // default: false
44
+ // },
45
+ trashed: {
46
+ type: Boolean,
47
+ index: true
48
+ },
49
+ secret: {
50
+ type: String,
51
+ required: true,
52
+ default: uuidv4(),
53
+ select: false
54
+ },
55
+ language: {
56
+ type: String,
57
+ required: false,
58
+ default: 'en'
59
+ // index: true
60
+ },
61
+ attributes: {
62
+ type: Object,
63
+ },
64
+ createdBy: {
65
+ type: String,
66
+ required: true
67
+ }
68
+ },{
69
+ timestamps: true
70
+ }
71
+ );
72
+
73
+ Faq_kbSchema.virtual('fullName').get(function () {
74
+ // winston.debug("faq_kb fullName virtual called");
75
+ return (this.name);
76
+ });
77
+
78
+ var faq_kb = mongoose.model('faq_kb', Faq_kbSchema);
79
+
80
+ if (process.env.MONGOOSE_SYNCINDEX) {
81
+ faq_kb.syncIndexes();
82
+ console.log("faq_kb syncIndexes");
83
+ }
84
+
85
+
86
+
87
+ module.exports = faq_kb
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@tiledesk/tiledesk-tybot-connector",
3
+ "version": "0.1.0",
4
+ "description": "Tiledesk Tybot connector",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "start": "node index.js"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "@tiledesk/tiledesk-chatbot-client": "^0.5.30",
15
+ "@tiledesk/tiledesk-chatbot-util": "^0.8.36",
16
+ "@tiledesk/tiledesk-client": "^0.8.28",
17
+ "@tiledesk/tiledesk-chatbot-plugs": "^0.1.1",
18
+ "axios": "^0.27.2",
19
+ "body-parser": "^1.19.0",
20
+ "express": "^4.17.1",
21
+ "jsonwebtoken": "^8.5.1",
22
+ "mongoose": "^6.3.5",
23
+ "nanoid": "^3.1.25",
24
+ "request": "^2.88.2",
25
+ "uuid": "^3.3.3",
26
+ "cors": "^2.8.5"
27
+ }
28
+ }
package/publish.sh ADDED
@@ -0,0 +1,10 @@
1
+ #npm version patch
2
+ version=`node -e 'console.log(require("./package.json").version)'`
3
+ echo "version $version"
4
+
5
+ if [ "$version" != "" ]; then
6
+ git tag -a "$version" -m "`git log -1 --format=%s`"
7
+ echo "Created a new tag, $version"
8
+ git push --tags
9
+ npm publish --access public
10
+ fi
@@ -0,0 +1,218 @@
1
+ const { TiledeskChatbotUtil } = require('@tiledesk/tiledesk-chatbot-util');
2
+ const { TiledeskClient } = require('@tiledesk/tiledesk-client');
3
+
4
+ class DirectivesChatbotPlug {
5
+
6
+ /**
7
+ * @example
8
+ * const { DirectivesChatbotPlug } = require('./DirectivesChatbotPlug');
9
+ *
10
+ */
11
+
12
+ constructor(supportRequest, API_URL, token) {
13
+ this.supportRequest = supportRequest;
14
+ this.API_URL = API_URL;
15
+ this.token = token;
16
+ }
17
+
18
+ exec(pipeline) {
19
+ let message = pipeline.message;
20
+ if (message.attributes && message.attributes.directives && message.attributes.directives == true) {
21
+ const message_text = message.text;
22
+ console.log("processing message:", message_text);
23
+ /*const message_text =
24
+ `We are looking for an operator..
25
+ -
26
+ JUST WAIT A MOMENT
27
+ \\agent`;*/
28
+ let parsed_result = TiledeskChatbotUtil.parseDirectives(message_text);
29
+ console.log("Message directives:", parsed_result);
30
+ console.log("Message text ripped from directives:", parsed_result.text);
31
+ if (parsed_result && parsed_result.directives && parsed_result.directives.length > 0) {
32
+ // do not process more intents. Process directives and return
33
+ const text = parsed_result.text;
34
+ message.text = text;
35
+ this.directives = parsed_result.directives;
36
+ pipeline.nextplug();
37
+ /*this.processDirectives( () => {
38
+ console.log("End process directives.");
39
+ pipeline.nextplug();
40
+ });*/
41
+ }
42
+ else {
43
+ pipeline.nextplug();
44
+ }
45
+ }
46
+ else {
47
+ pipeline.nextplug();
48
+ return;
49
+ }
50
+
51
+ }
52
+
53
+
54
+
55
+ moveToDepartment(tdclient, requestId, depName, callback) {
56
+ tdclient.getAllDepartments((err, deps) => {
57
+ console.log("deps:", deps, err);
58
+ if (err) {
59
+ console.error("getAllDepartments() error:", err);
60
+ callback(err);
61
+ return;
62
+ }
63
+ let dep = null;
64
+ for(i=0; i < deps.length; i++) {
65
+ d = deps[i];
66
+ if (d.name.toLowerCase() === depName.toLowerCase()) {
67
+ dep = d;
68
+ break;
69
+ }
70
+ }
71
+ if (dep) {
72
+ tdclient.updateRequestDepartment(requestId, dep._id, null, (err) => {
73
+ if (err) {
74
+ console.error("An error:", err);
75
+ callback(err);
76
+ }
77
+ else {
78
+ callback();
79
+ }
80
+ });
81
+ }
82
+ });
83
+ }
84
+
85
+ processDirectives(theend) {
86
+
87
+ const directives = this.directives;
88
+ if (!directives || directives.length === 0) {
89
+ console.log("No directives to process.");
90
+ return;
91
+ }
92
+ const supportRequest = this.supportRequest;
93
+ const token = this.token;
94
+ const API_URL = this.API_URL;
95
+
96
+ const requestId = supportRequest.request_id
97
+ const depId = supportRequest.department._id;
98
+ const projectId = supportRequest.id_project;
99
+ const tdclient = new TiledeskClient({
100
+ projectId: projectId,
101
+ token: token,
102
+ APIURL: API_URL,
103
+ APIKEY: "___",
104
+ log:false
105
+ });
106
+
107
+ let i = -1;
108
+ console.log("processing directives:", directives);
109
+ function process(directive) {
110
+ if (directive) {
111
+ console.log("directive:", directive);
112
+ console.log("directive.name:", directive.name);
113
+ }
114
+ let directive_name = null;
115
+ if (directive && directive.name) {
116
+ directive_name = directive.name.toLowerCase();
117
+ }
118
+ if (directive == null) {
119
+ theend();
120
+ }
121
+ else if (directive_name === TiledeskChatbotUtil.DEPARTMENT_DIRECTIVE) {
122
+ let dep_name = "default department";
123
+ if (directive.parameter) {
124
+ dep_name = directive.parameter;
125
+ }
126
+ console.log("department:", dep_name);
127
+ moveToDepartment(tdclient, requestId, dep_name, () => {
128
+ console.log("moved to department:", dep_name);
129
+ process(nextDirective());
130
+ });
131
+ }
132
+ else if (directive_name === TiledeskChatbotUtil.HMESSAGE_DIRECTIVE) {
133
+ if (directive.parameter) {
134
+ let text = directive.parameter.trim();
135
+ let message = {
136
+ text: text,
137
+ attributes: {
138
+ subtype: "info"
139
+ }
140
+ };
141
+ console.log("Message:", message)
142
+ tdclient.sendSupportMessage(requestId, message, () => {
143
+ process(nextDirective());
144
+ });
145
+ }
146
+ }
147
+ else if (directive_name === TiledeskChatbotUtil.MESSAGE_DIRECTIVE) {
148
+ if (directive.parameter) {
149
+ let text = directive.parameter.trim();
150
+ let message = {text: text};
151
+ console.log("text.lastIndexOf(hide)", text.lastIndexOf("\\hide"))
152
+ if (text.lastIndexOf("\\hide") >= 0) {
153
+ console.log("HIDDEN");
154
+ message.text = text.slice(0, text.lastIndexOf("\\hide")).trim();
155
+ message.attributes = {
156
+ subtype: "info"
157
+ }
158
+ }
159
+ console.log("Message:", message)
160
+ tdclient.sendSupportMessage(requestId, message, () => {
161
+ process(nextDirective());
162
+ });
163
+ }
164
+ }
165
+ else if (directive_name === "\\agent") {
166
+ console.log("assign to request:", requestId);
167
+ console.log("assign to dep:", depId);
168
+ console.log("assign to dep name:", supportRequest.department.name);
169
+ tdclient.log = true;
170
+ tdclient.agent(requestId, depId, (err) => {
171
+ if (err) {
172
+ console.error("Error moving to agent:", err);
173
+ }
174
+ else {
175
+ console.log("Successfully moved to agent");
176
+ }
177
+ process(nextDirective());
178
+ });
179
+ }
180
+ else if (directive_name === "\\removecurrentbot") {
181
+ console.log("assign to request:", requestId);
182
+ console.log("assign to dep:", depId);
183
+ console.log("assign to dep name:", request.department.name);
184
+ tdclient.log = true;
185
+ tdclient.removeCurrentBot(requestId, (err) => {
186
+ if (err) {
187
+ console.error("Error removeCurrentBot():", err);
188
+ }
189
+ else {
190
+ console.log("Successfully removeCurrentBot()");
191
+ }
192
+ process(nextDirective());
193
+ });
194
+ }
195
+ else {
196
+ console.log("Unknown directive:", directive.name);
197
+ process(nextDirective());
198
+ }
199
+ }
200
+ process(nextDirective());
201
+
202
+ function nextDirective() {
203
+ i += 1;
204
+ console.log("i:", i);
205
+ if (i < directives.length) {
206
+ let nextd = directives[i];
207
+ console.log("next:", nextd);
208
+ return nextd;
209
+ }
210
+ else {
211
+ return null;
212
+ }
213
+ }
214
+ }
215
+
216
+ }
217
+
218
+ module.exports = { DirectivesChatbotPlug };
@@ -0,0 +1,85 @@
1
+ const { TiledeskChatbotUtil } = require('@tiledesk/tiledesk-chatbot-util');
2
+
3
+ class MarkbotChatbotPlug {
4
+
5
+
6
+ /**
7
+ * @example
8
+ * const { MarkbotChatbotPlug } = require('./MarkbotChatbotPlug');
9
+ *
10
+ */
11
+
12
+ constructor() {
13
+ }
14
+
15
+ exec(pipeline) {
16
+ let message = pipeline.message;
17
+ console.log("markbot, message.attributes", message.attributes)
18
+ if (message.attributes && message.attributes.markbot != undefined && message.attributes.markbot == false) {
19
+ console.log("markbot disabled")
20
+ pipeline.nextplug();
21
+ return;
22
+ }
23
+ // the legacy 'microlanguage' command
24
+ /*if (message.attributes && message.attributes.microlanguage != undefined && message.attributes.microlanguage == false) {
25
+ console.log("microlanguage disabled");
26
+ pipeline.nextplug();
27
+ return;
28
+ }*/
29
+
30
+ if (!message.attributes) {
31
+ message.attributes = {}
32
+ }
33
+
34
+ if (message.text) {
35
+ console.log("markbotting main message...");
36
+ let parsed_reply = TiledeskChatbotUtil.parseReply(message.text);
37
+ console.log("parsed", JSON.stringify(parsed_reply));
38
+ if (parsed_reply) {
39
+ message.text = parsed_reply.message.text;
40
+ message.type = parsed_reply.message.type;
41
+ message.metadata = parsed_reply.message.metadata;
42
+ console.log("parsed_reply.message.attributes", parsed_reply.message.attributes);
43
+ if (parsed_reply.message.attributes) {
44
+ for(const [key, value] of Object.entries(parsed_reply.message.attributes)) {
45
+ console.log("key:", key)
46
+ console.log("value:", value)
47
+
48
+ message.attributes[key] = value;
49
+ }
50
+ }
51
+ //message.attributes.attachment = parsed_reply.message.attributes.attachment;
52
+ }
53
+ }
54
+
55
+ if (message.attributes && message.attributes.commands) {
56
+ let commands = message.attributes.commands;
57
+ console.log("commands for markbot:", commands);
58
+ if (commands.length > 1) {
59
+ for (let i = 0; i < commands.length; i++) {
60
+ if (commands[i].type === 'message' && commands[i].message && commands[i].message.text) {
61
+ let parsed_reply = TiledeskChatbotUtil.parseReply(commands[i].message.text);
62
+ //console.log("PARSED***", parsed_reply);
63
+ commands[i].message = parsed_reply.message;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ console.log("Message out of Markbot:", JSON.stringify(message));
69
+ pipeline.nextplug(pipeline);
70
+
71
+ }
72
+
73
+ /*
74
+ next(pipeline, completionCallback) {
75
+ const plug = pipeline.nextplug();
76
+ if (!plug) {
77
+ completionCallback();
78
+ }
79
+ else {
80
+ plug.exec(pipeline);
81
+ }
82
+ }*/
83
+ }
84
+
85
+ module.exports = { MarkbotChatbotPlug };
@@ -0,0 +1,116 @@
1
+ class MessagePipeline {
2
+
3
+ /**
4
+ * @example
5
+ * const { MessagePipeline } = require('./MessagePipeline');
6
+ *
7
+ */
8
+
9
+ constructor(message, context) {
10
+ this.context = context;
11
+ this.message = message;
12
+ this.plugs = [];
13
+ this.counter = -1;
14
+ }
15
+
16
+ addPlug(plug) {
17
+ this.plugs.push(plug);
18
+ }
19
+
20
+ exec(completionCallback) {
21
+ this.completionCallback = completionCallback;
22
+ return new Promise((resolve, reject) => {
23
+ this.resolve = resolve;
24
+ this.reject = reject;
25
+ this.nextplug();
26
+ });
27
+ }
28
+
29
+ /*
30
+ exec(completionCallback) {
31
+ this.completionPromise = new Promise((resolve, reject) => {
32
+ console.log("The context", context)
33
+ const plug = this.nextplug();
34
+ if (!plug) {
35
+ console.log("NO PLUGS!");
36
+ if (completionCallback) {
37
+ console.log("completionCallback found.");
38
+ completionCallback();
39
+ }
40
+ console.log("Resolving...", this.message);
41
+ return resolve();
42
+ }
43
+ else {
44
+ plug.exec(this, () => {
45
+ if (completionCallback) {
46
+ console.log("completionCallback found.");
47
+ completionCallback();
48
+ }
49
+ console.log("Resolving...", this.message);
50
+ return resolve();
51
+ });
52
+ }
53
+ });
54
+ return this.completionPromise;
55
+ }
56
+ */
57
+
58
+ /*execOn(message, context, completionCallback) {
59
+ return new Promise((resolve, reject) => {
60
+ this.message = message;
61
+ console.log("The context", context)
62
+ this.process(this.nextplug(), context, (message) => {
63
+ console.log("All plugs processed.", message)
64
+ if (completionCallback) {
65
+ console.log("completionCallback found.")
66
+ completionCallback(message);
67
+ }
68
+ console.log("Resolving...", message)
69
+ return resolve(message);
70
+ });
71
+ });
72
+ }*/
73
+
74
+ /*process(plug, context, completionCallback) {
75
+ if (plug) {
76
+ plug.execOn(this.message, context, () => {
77
+ this.process(this.nextplug(), context, completionCallback);
78
+ });
79
+ }
80
+ else {
81
+ completionCallback(this.message);
82
+ }
83
+ }*/
84
+
85
+ nextplug() {
86
+ this.counter += 1;
87
+ console.log(`processing plug[${this.counter}]`);
88
+ if (this.counter < this.plugs.length) {
89
+ console.log("Still plugs...")
90
+ let nextp = this.plugs[this.counter];
91
+ console.log("nextp is:", nextp)
92
+ nextp.exec(this);
93
+ }
94
+ else {
95
+ console.log("no more plugs");
96
+ this.resolve(this.message);
97
+ }
98
+ }
99
+ /*
100
+ nextplug() {
101
+ this.counter += 1;
102
+ console.log(`processing plug[${this.counter}]`);
103
+ if (this.counter < this.plugs.length) {
104
+ let nextp = this.plugs[this.counter];
105
+ return nextp;
106
+ }
107
+ else {
108
+ console.log("no more plugs");
109
+ return null;
110
+ }
111
+ }
112
+ */
113
+
114
+ }
115
+
116
+ module.exports = { MessagePipeline };
@@ -0,0 +1,48 @@
1
+ const { TiledeskChatbotUtil } = require('@tiledesk/tiledesk-chatbot-util');
2
+
3
+ class SplitsChatbotPlug {
4
+
5
+
6
+ /**
7
+ * @example
8
+ * const { SplitsChatbotPlug } = require('./SplitsChatbotPlug');
9
+ *
10
+ */
11
+
12
+ constructor() {
13
+ }
14
+
15
+ exec(pipeline) {
16
+ let message = pipeline.message;
17
+ if (message.attributes && message.attributes.splits && message.attributes.splits == false) {
18
+ completionCallback(message);
19
+ return;
20
+ }
21
+ console.log("Splitting...")
22
+ // if splits found just a attributs.commands payload is attached
23
+ // to the original json message with split commands
24
+ let commands = TiledeskChatbotUtil.splitPars(message.text);
25
+ console.log("commands", commands)
26
+ if (commands && commands.length > 1) {
27
+ if (!message.attributes) {
28
+ message.attributes = {}
29
+ }
30
+ message.attributes.commands = commands;
31
+ }
32
+ console.log("Message out of Splits plugin:", JSON.stringify(message));
33
+ pipeline.nextplug();
34
+ //next(pipeline, completionCallback);
35
+ }
36
+
37
+ next(pipeline, completionCallback) {
38
+ const plug = pipeline.nextplug();
39
+ if (!plug) {
40
+ completionCallback();
41
+ }
42
+ else {
43
+ plug.exec(pipeline);
44
+ }
45
+ }
46
+ }
47
+
48
+ module.exports = { SplitsChatbotPlug };
@@ -0,0 +1,182 @@
1
+ let axios = require('axios');
2
+
3
+ class WebhookChatbotPlug {
4
+
5
+ /**
6
+ * @example
7
+ * const { DirectivesChatbotPlug } = require('./DirectivesChatbotPlug');
8
+ *
9
+ */
10
+
11
+ constructor(supportRequest, webhookurl, token) {
12
+ this.supportRequest = supportRequest;
13
+ this.webhookurl = webhookurl;
14
+ this.token = token;
15
+ }
16
+
17
+ exec(pipeline) {
18
+ let message = pipeline.message;
19
+ let context = pipeline.context;
20
+ console.log("EXECUTIN WEBHOOK", message.attributes)
21
+ console.log("EXECUTIN WEBHOOK.webhook", message.attributes.webhook)
22
+ console.log("EXECUTIN WEBHOOK", message.attributes)
23
+ if (message.attributes && message.attributes.webhook && message.attributes.webhook === true) {
24
+ console.log("EXECUTING WEBHOOK!", this.webhookurl);
25
+ this.execWebhook(message, context, this.webhookurl, (err, message_from_webhook) => {
26
+ console.log("Err", err)
27
+ console.log("message", message_from_webhook)
28
+ if (err) {
29
+ console.error("Error calling webhook", this.webhookurl)
30
+ pipeline.nextplug();
31
+ }
32
+ else {
33
+ console.log("Webhook successfully end:", message_from_webhook);
34
+ pipeline.message = message_from_webhook;
35
+ pipeline.nextplug();
36
+ }
37
+ });
38
+ }
39
+ else {
40
+ console.log("NO WEBHOOK!");
41
+ pipeline.nextplug();
42
+ return;
43
+ }
44
+ console.log("Start processing webhook...");
45
+ }
46
+
47
+ execWebhook(reply_message, context, webhookurl, callback) {
48
+ console.log("WEBHOOK. on context", context)
49
+ console.log("WEBHOOK. on message", reply_message)
50
+ const HTTPREQUEST = {
51
+ url: webhookurl,
52
+ headers: {
53
+ 'Content-Type' : 'application/json',
54
+ 'User-Agent': 'tiledesk-bot',
55
+ 'Origin': "pre"
56
+ },
57
+ json: context,
58
+ method: 'POST'
59
+ };
60
+ WebhookChatbotPlug.myrequest(
61
+ HTTPREQUEST,
62
+ function(err, res) {
63
+ if (err || (res && res.status >= 400) || (res && !res.data)) {
64
+ console.error("An error occurred calling intent's webhook url:", webhookurl);
65
+ if (callback) {
66
+ callback(reply_message);
67
+ }
68
+ }
69
+ else {
70
+ console.log("Hello", res.data)
71
+ if (callback) {
72
+ callback(null, res.data);
73
+ }
74
+ }
75
+ }, this.log
76
+ );
77
+
78
+ /*
79
+ return request({
80
+ uri : webhookurl,
81
+ headers: {
82
+ 'Content-Type' : 'application/json',
83
+ 'User-Agent': 'tiledesk-bot',
84
+ 'Origin': webhook_origin
85
+ //'x-hook-secret': s.secret
86
+ },
87
+ method: 'POST',
88
+ json: true,
89
+ body: {payload:{text: text, bot: bot, message: message, intent: faq}, token: token},
90
+ // }).then(response => {
91
+ }, function(err, response, json){
92
+ if (err) {
93
+ winston.error("Error from webhook reply of getParsedMessage. Return standard reply", err);
94
+
95
+ return resolve(messageReply);
96
+
97
+ // return error
98
+
99
+ }
100
+ if (response.statusCode >= 400) {
101
+ winston.verbose("The ChatBot webhook return error http status code. Return standard reply", response);
102
+ return resolve(messageReply);
103
+ }
104
+
105
+ if (!json) { //the webhook return empty body
106
+ winston.verbose("The ChatBot webhook return no json. Return standard reply", response);
107
+ return resolve(messageReply);
108
+ }
109
+
110
+ winston.debug("webhookurl repl_message ", response);
111
+
112
+ var text = undefined;
113
+ if(json && json.text===undefined) {
114
+ winston.verbose("webhookurl json is defined but text not. return standard reply",{json:json, response:response});
115
+ // text = 'Field text is not defined in the webhook respose of the faq with id: '+ faq._id+ ". Error: " + JSON.stringify(response);
116
+ return resolve(messageReply);
117
+ }else {
118
+ text = json.text;
119
+ }
120
+ winston.debug("webhookurl text: "+ text);
121
+
122
+ // // let cloned_message = Object.assign({}, messageReply);
123
+ // let cloned_message = message;
124
+ // winston.debug("cloned_message : ",cloned_message);
125
+
126
+ // if (json.attributes) {
127
+ // if (!cloned_message.attributes) {
128
+ // cloned_message.attributes = {}
129
+ // }
130
+ // winston.debug("ChatBot webhook json.attributes: ",json.attributes);
131
+ // for(const [key, value] of Object.entries(json.attributes)) {
132
+ // cloned_message.attributes[key] = value
133
+ // }
134
+ // }
135
+
136
+ // winston.debug("cloned_message after attributes: ",cloned_message);
137
+
138
+ that.parseMicrolanguage(text, message, bot, faq, true, json).then(function(bot_answer) {
139
+ return resolve(bot_answer);
140
+ });
141
+ });
142
+ */
143
+ }
144
+
145
+ // ************************************************
146
+ // ****************** HTTP REQUEST ****************
147
+ // ************************************************
148
+
149
+ static myrequest(options, callback, log) {
150
+ if (log) {
151
+ console.log("API URL:", options.url);
152
+ console.log("** Options:", options);
153
+ }
154
+ axios(
155
+ {
156
+ url: options.url,
157
+ method: options.method,
158
+ data: options.json,
159
+ headers: options.headers
160
+ })
161
+ .then(function (res) {
162
+ if (log) {
163
+ console.log("Response for url:", options.url);
164
+ console.log("Response headers:\n", res.headers);
165
+ console.log("******** Response for url:", res);
166
+ console.log("Response body:\n", res.data);
167
+ }
168
+ if (callback) {
169
+ callback(null, res);
170
+ }
171
+ })
172
+ .catch(function (error) {
173
+ console.error("An error occurred:", error);
174
+ if (callback) {
175
+ callback(error, null, null);
176
+ }
177
+ });
178
+ }
179
+
180
+ }
181
+
182
+ module.exports = { WebhookChatbotPlug };
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@tiledesk/tiledesk-chatbot-plugs",
3
+ "version": "0.1.1",
4
+ "description": "Tiledesk Chatbot Plugs",
5
+ "engines": {
6
+ "node": ">=12.x"
7
+ },
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/Tiledesk/tiledesk-chatbot.git"
15
+ },
16
+ "author": "Andrea Sponziello",
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/Tiledesk/tiledesk-chatbot/issues"
20
+ },
21
+ "homepage": "https://github.com/Tiledesk/tiledesk-chatbot#readme",
22
+ "dependencies": {
23
+ "@tiledesk/tiledesk-chatbot-util": "^0.8.36",
24
+ "@tiledesk/tiledesk-client": "^0.8.28",
25
+ "axios": "^0.27.2"
26
+ }
27
+ }
@@ -0,0 +1,10 @@
1
+ #npm version patch
2
+ version=`node -e 'console.log(require("./package.json").version)'`
3
+ echo "version $version"
4
+
5
+ if [ "$version" != "" ]; then
6
+ git tag -a "$version" -m "`git log -1 --format=%s`"
7
+ echo "Created a new tag, $version"
8
+ git push --tags
9
+ npm publish --access public
10
+ fi