@tiledesk/tiledesk-tybot-connector 0.1.96 → 0.1.98

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/CHANGELOG.md CHANGED
@@ -5,11 +5,17 @@
5
5
  available on:
6
6
  ▶️ https://www.npmjs.com/package/@tiledesk/tiledesk-tybot-connector
7
7
 
8
+ ### 0.1.98 - online
9
+ - added default EX:86400 to exprire time to tdcache.set()
10
+ - added "chatbot" instance to DirectivesChatbotPlug instance created in index.js
11
+ - added Capture user Reply
12
+ - added triggerBot to DirDepartment
13
+
8
14
  ### 0.1.96 - online
9
15
 
10
16
  ### 0.1.95
11
- - added "chatbot" instance to DirectivesChatbotPlug instance created in index.js
12
17
  - Fix: missing key API_ENDPOINT in production
18
+ - added "message: message" to DirectivesChatbotPlug instance created in index.js
13
19
 
14
20
  ### 0.1.94
15
21
  - Fix: last_user_message on _tdInternal sender
package/TdCache.js CHANGED
@@ -39,6 +39,9 @@ class TdCache {
39
39
 
40
40
  async set(key, value, options) {
41
41
  //console.log("setting key value", key, value)
42
+ if (!options) {
43
+ options = {EX: 86400}
44
+ }
42
45
  return new Promise( async (resolve, reject) => {
43
46
  if (options && options.EX) {
44
47
  //console.log("expires:", options.EX)
package/index.js CHANGED
@@ -151,27 +151,38 @@ router.post('/ext/:botid', async (req, res) => {
151
151
  }
152
152
 
153
153
  // console.log("reply is:", reply);
154
+ // if (reply.attributes.intent_info.intent_id) {
155
+ // process.exit(1)
156
+ // }
154
157
  if (reply.actions && reply.actions.length > 0) { // structured actions (coming from chatbot designer)
155
158
  if (log) {console.log("the actions:", JSON.stringify(reply.actions));}
156
159
  let directives = actionsToDirectives(reply.actions);
157
160
  if (log) {console.log("the directives:", JSON.stringify(directives));}
158
- let directivesPlug = new DirectivesChatbotPlug(
159
- {
160
- reply: reply,
161
- directives: directives,
162
- chatbot: chatbot,
163
- supportRequest: message.request,
164
- TILEDESK_API_ENDPOINT: APIURL,
165
- TILEBOT_ENDPOINT:process.env.TYBOT_ENDPOINT,
166
- token: token,
167
- log: log,
168
- HELP_CENTER_API_ENDPOINT: process.env.HELP_CENTER_API_ENDPOINT,
169
- cache: tdcache
170
- }
171
- );
172
- directivesPlug.processDirectives( () => {
173
- if (log) {console.log("Actions - Directives executed.");}
174
- });
161
+ try {
162
+
163
+
164
+ let directivesPlug = new DirectivesChatbotPlug(
165
+ {
166
+ message: message,
167
+ reply: reply,
168
+ directives: directives,
169
+ chatbot: chatbot,
170
+ supportRequest: message.request,
171
+ TILEDESK_API_ENDPOINT: APIURL,
172
+ TILEBOT_ENDPOINT:process.env.TYBOT_ENDPOINT,
173
+ token: token,
174
+ log: log,
175
+ HELP_CENTER_API_ENDPOINT: process.env.HELP_CENTER_API_ENDPOINT,
176
+ cache: tdcache
177
+ }
178
+ );
179
+ directivesPlug.processDirectives( () => {
180
+ if (log) {console.log("Actions - Directives executed.");}
181
+ });
182
+ }
183
+ catch (error) {
184
+ console.error("an error:", error);
185
+ }
175
186
  }
176
187
  else { // text answer (parse text directives to get actions)
177
188
  if (log) {console.log("an answer:", reply.text);}
@@ -62,7 +62,14 @@ class IntentForm {
62
62
  */
63
63
  async getMessage(user_text) {
64
64
  //console.log("get message:", user_text)
65
+ let current_form = null;
66
+ const _current_form = await this.getValue(this.CURRENT_FORM_KEY);
67
+ if (_current_form) {
68
+ current_form = JSON.parse(_current_form);
69
+ }
70
+ //console.log("CURRENT FORM IS", current_form);
65
71
  if (
72
+ current_form &&
66
73
  this.form &&
67
74
  this.form.cancelCommands &&
68
75
  this.form.cancelCommands.includes(user_text.toLowerCase())) {
@@ -81,12 +88,7 @@ class IntentForm {
81
88
  if (_current_field) {
82
89
  current_field = Number(_current_field);
83
90
  }
84
- let current_form = null;
85
- const _current_form = await this.getValue(this.CURRENT_FORM_KEY);
86
- if (_current_form) {
87
- current_form = JSON.parse(_current_form);
88
- }
89
- //console.log("CURRENT FORM IS", current_form);
91
+
90
92
  if (current_field == null) {
91
93
  if (this.log) {console.log("current_field is undefined")}
92
94
  current_field = 0;
@@ -63,6 +63,30 @@ class TiledeskChatbot {
63
63
  console.log("replyToMessage() > lead found:", JSON.stringify(lead));
64
64
  }
65
65
 
66
+ // reset lockedIntent on direct user invocation ( /intent or action => this only?)
67
+ if (message.sender != "_tdinternal") {
68
+ try {
69
+ // if (this.log) {console.log("Checking locked intent reset on explicit intent invokation.");}
70
+ // if (message.text.startsWith("/")) {
71
+ // if (this.log) {console.log("RESETTING LOCKED INTENT. Intent was explicitly invoked with / command...", message.text);}
72
+ // await this.unlockIntent(this.requestId);
73
+ // await this.unlockAction(this.requestId);
74
+ // if (this.log) {console.log("RESET LOCKED INTENT. Intent was explicitly invoked with / command:", message.text);}
75
+ // }
76
+ if (this.log) {console.log("Checking locked intent reset on action invocation.");}
77
+ if (message.attributes && message.attributes.action) {
78
+ if (this.log) {console.log("Message has action:", message.attributes.action)}
79
+ if (this.log) {console.log("RESETTING LOCKED INTENT. Intent was explicitly invoked with an action:", message.attributes.action);}
80
+ await this.unlockIntent(this.requestId);
81
+ await this.unlockAction(this.requestId);
82
+ if (this.log) {console.log("RESET LOCKED INTENT. Intent was explicitly invoked with an action:", message.attributes.action);}
83
+ }
84
+ }
85
+ catch(error) {
86
+ console.error("Error resetting locked intent:", error);
87
+ }
88
+ }
89
+
66
90
  // any external invocation restarts the steps counter
67
91
  if (message.sender != "_tdinternal") {
68
92
  if (this.log) {
@@ -475,6 +499,7 @@ class TiledeskChatbot {
475
499
  delete question_payload.request;
476
500
  const intent_info = {
477
501
  intent_name: answerObj.intent_display_name,
502
+ intent_id: answerObj.intent_id,
478
503
  is_fallback: false,
479
504
  confidence: answerObj.score,
480
505
  question_payload: question_payload,
@@ -500,8 +525,8 @@ class TiledeskChatbot {
500
525
  return bot_answer;
501
526
  }
502
527
 
528
+
503
529
  async lockIntent(requestId, intent_name) {
504
- // await this.tdcache.set("tilebot:requests:" + requestId + ":locked", intent_name);
505
530
  await DirLockIntent.lockIntent(this.tdcache, requestId, intent_name);
506
531
  }
507
532
 
@@ -516,7 +541,29 @@ class TiledeskChatbot {
516
541
 
517
542
  async unlockIntent(requestId) {
518
543
  await DirUnlockIntent.unlockIntent(this.tdcache, requestId);
519
- // await this.tdcache.del("tilebot:requests:" + requestId + ":locked");
544
+ }
545
+
546
+ async lockAction(requestId, action_id) {
547
+ if (this.tdcache != null && requestId != null && action_id != null) {
548
+ await this.tdcache.set("tilebot:requests:" + requestId + ":action:locked", action_id);
549
+
550
+ }
551
+ else {
552
+ console.error("lockAction recoverable error, one of requestId:", requestId, "action_id:", action_id, "is null");
553
+ }
554
+ }
555
+
556
+ async currentLockedAction(requestId) {
557
+ if (this.tdcache) {
558
+ return await this.tdcache.get("tilebot:requests:" + requestId + ":action:locked");
559
+ }
560
+ else {
561
+ return null;
562
+ }
563
+ }
564
+
565
+ async unlockAction(requestId) {
566
+ await this.tdcache.del("tilebot:requests:" + requestId + ":action:locked");
520
567
  }
521
568
 
522
569
  async addParameter(parameter_name, parameter_value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-tybot-connector",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "description": "Tiledesk Tybot connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -36,6 +36,8 @@ const { DirIfOnlineAgents } = require('./directives/DirIfOnlineAgents');
36
36
  const { DirReply } = require('./directives/DirReply');
37
37
  const { DirRandomReply } = require('./directives/DirRandomReply');
38
38
  const { DirGptTask } = require('./directives/DirGptTask');
39
+ const { DirForm } = require('./directives/DirForm');
40
+ const { DirCaptureUserReply } = require('./directives/DirCaptureUserReply');
39
41
 
40
42
  class DirectivesChatbotPlug {
41
43
 
@@ -56,6 +58,7 @@ class DirectivesChatbotPlug {
56
58
  this.directives = config.directives;
57
59
  this.reply = config.reply;
58
60
  this.chatbot = config.chatbot;
61
+ this.message = config.message;
59
62
  // console.log("We have the support request:", JSON.stringify(this.supportRequest))
60
63
  }
61
64
 
@@ -103,7 +106,7 @@ class DirectivesChatbotPlug {
103
106
  return;
104
107
  }
105
108
  const supportRequest = this.supportRequest;
106
- // console.log("supportRequest is:", JSON.stringify(supportRequest))
109
+ console.log("supportRequest is:", JSON.stringify(supportRequest))
107
110
 
108
111
  const token = this.token;
109
112
  const API_URL = this.API_URL;
@@ -128,6 +131,8 @@ class DirectivesChatbotPlug {
128
131
 
129
132
  this.context = {
130
133
  projectId: projectId,
134
+ chatbot: this.chatbot,
135
+ message: this.message,
131
136
  token: token,
132
137
  supportRequest: supportRequest,
133
138
  reply: this.reply,
@@ -194,12 +199,11 @@ class DirectivesChatbotPlug {
194
199
  }
195
200
 
196
201
  async process(directive) {
197
- // console.log(".process(directive):", JSON.stringify(directive));
202
+ console.log(".process(directive):", JSON.stringify(directive));
198
203
  let context = this.context;
199
204
  // console.log(".this.context.reply", JSON.stringify(this.context.reply));
200
205
  if (directive) {
201
206
  if (context.log) {
202
- console.log("..process(directive):", JSON.stringify(directive));
203
207
  console.log("directive['name']:", directive["name"]);
204
208
  }
205
209
  }
@@ -207,6 +211,29 @@ class DirectivesChatbotPlug {
207
211
  if (directive && directive.name) {
208
212
  directive_name = directive.name.toLowerCase();
209
213
  }
214
+ if (directive && directive.action) {
215
+ console.log("Checking locks", JSON.stringify(directive));
216
+ // try {
217
+ const action_id = directive.action["_tdActionId"];
218
+ console.log("Checking locked directive:", action_id, "for request:", this.supportRequest.request_id);
219
+ const locked_action_id = await this.chatbot.currentLockedAction(this.supportRequest.request_id);
220
+ console.log("locked_action_id:", locked_action_id);
221
+ if ( locked_action_id && (locked_action_id !== action_id) ) {
222
+ console.log("Found locked action:", locked_action_id, "Skipping this action:", action_id);
223
+ let next_dir = await this.nextDirective(this.directives);
224
+ this.process(next_dir);
225
+ return;
226
+ }
227
+ else {
228
+ // go on
229
+ console.log("Going on to next directive...");
230
+ }
231
+ // }
232
+ // catch(error) {
233
+ // console.error("Error on locks:", error);
234
+ // }
235
+
236
+ }
210
237
  if (directive == null || (directive !== null && directive["name"] === undefined)) {
211
238
  if (context.log) { console.log("stop process(). directive is (null?):", directive);}
212
239
  this.theend();
@@ -243,7 +270,7 @@ class DirectivesChatbotPlug {
243
270
  });
244
271
  }
245
272
  else if (directive_name === Directives.REPLY) {
246
- // console.log("...DirReply");
273
+ console.log("...DirReply");
247
274
  new DirReply(context).execute(directive, async () => {
248
275
  let next_dir = await this.nextDirective(this.directives);
249
276
  this.process(next_dir);
@@ -493,6 +520,27 @@ class DirectivesChatbotPlug {
493
520
  }
494
521
  });
495
522
  }
523
+ else if (directive_name === Directives.FORM) {
524
+ console.log("...DirForm");
525
+ new DirForm(context).execute(directive, async (stop) => {
526
+ if (context.log) { console.log("stop on form?", stop);}
527
+ if (stop == true) {
528
+ if (context.log) { console.log("Stopping Actions on:", JSON.stringify(directive));}
529
+ this.theend();
530
+ }
531
+ else {
532
+ let next_dir = await this.nextDirective(this.directives);
533
+ this.process(next_dir);
534
+ }
535
+ });
536
+ }
537
+ else if (directive_name === Directives.CAPTURE_USER_REPLY) {
538
+ console.log("...DirCaptureUserReply");
539
+ new DirCaptureUserReply(context).execute(directive, async () => {
540
+ let next_dir = await this.nextDirective(this.directives);
541
+ this.process(next_dir);
542
+ });
543
+ }
496
544
  else if (directive_name === Directives.CODE) {
497
545
  // console.log("...DirCode", directive);
498
546
  new DirCode(context).execute(directive, async () => {
@@ -75,7 +75,8 @@ class DirAskGPT {
75
75
  console.log("falseIntentAttributes",falseIntentAttributes )
76
76
  }
77
77
 
78
- const kb_url = process.env.API_ENDPOINT + "/" + this.context.projectId + "/kbsettings";
78
+ const server_base_url = process.env.API_ENDPOINT || process.env.API_URL;
79
+ const kb_url = server_base_url + "/" + this.context.projectId + "/kbsettings";
79
80
  if (this.log) { console.log("ApiEndpoint URL: ", kb_url); }
80
81
  const KB_HTTPREQUEST = {
81
82
  url: kb_url,
@@ -0,0 +1,128 @@
1
+ const { Filler } = require('../Filler');
2
+ const { TiledeskChatbot } = require('../../models/TiledeskChatbot');
3
+ const { DirIntent } = require('./DirIntent');
4
+
5
+ class DirCaptureUserReply {
6
+ constructor(context) {
7
+ if (!context) {
8
+ throw new Error('context object is mandatory.');
9
+ }
10
+ this.context = context;
11
+ this.reply = context.reply;
12
+ // reply = {
13
+ // actions: [
14
+ // {
15
+ // _tdActionType: 'askgpt',
16
+ // _tdActionTitle: 'gpt action',
17
+ // assignReplyTo: 'gpt_reply',
18
+ // assignSourceTo: 'gpt_source',
19
+ // kbid: 'XXX',
20
+ // trueIntent: '#SUCCESS',
21
+ // falseIntent: '#FAILURE',
22
+ // question: 'this is the question: ${last_user_message}'
23
+ // }
24
+ // ],
25
+ // attributes: {
26
+ // clienttimestamp: 1695548792706,
27
+ // intent_info: {
28
+ // intent_name: 'gpt success',
29
+ // intent_id: '00f93b97-89ee-466d-a09c-e47a18943057',
30
+ // is_fallback: false,
31
+ // confidence: undefined,
32
+ // question_payload: [Object],
33
+ // botId: 'botID',
34
+ // bot: [Object]
35
+ // },
36
+ // webhook: false
37
+ // }
38
+ // }
39
+ this.message = context.message;
40
+ this.tdclient = context.tdclient;
41
+ this.chatbot = context.chatbot;
42
+ this.tdcache = context.tdcache;
43
+ this.requestId = context.requestId;
44
+ this.intentDir = new DirIntent(context);
45
+ this.log = context.log;
46
+ }
47
+
48
+ execute(directive, callback) {
49
+ let action;
50
+ if (directive.action) {
51
+ action = directive.action;
52
+ }
53
+ else {
54
+ console.error("Incorrect directive:", JSON.stringify(directive));
55
+ callback();
56
+ return;
57
+ }
58
+ this.go(action, () => {
59
+ callback();
60
+ });
61
+ }
62
+
63
+ async go(action, callback) {
64
+ const goToIntent = action.goToIntent;
65
+ console.log("(DirCaptureUserReply) goToIntent:", goToIntent);
66
+ let lockedAction = await this.chatbot.currentLockedAction(this.requestId);
67
+ console.log("(DirCaptureUserReply) lockedAction:", lockedAction);
68
+ if (!lockedAction) {
69
+ console.log("(DirCaptureUserReply) !lockedAction");
70
+ const intent_name = this.reply.attributes.intent_info.intent_name
71
+ const actionId = action["_tdActionId"];
72
+ console.log("(DirCaptureUserReply) intent_name:", intent_name);
73
+ console.log("(DirCaptureUserReply) actionId:", actionId);
74
+ await this.chatbot.lockIntent(this.requestId, intent_name);
75
+ console.log("(DirCaptureUserReply) lockIntent");
76
+ await this.chatbot.lockAction(this.requestId, actionId);
77
+ console.log("(DirCaptureUserReply) lockAction");
78
+ let _lockedAction = await this.chatbot.currentLockedAction(this.requestId);
79
+ let _lockedIntent = await this.chatbot.currentLockedIntent(this.requestId);
80
+ console.log("(DirCaptureUserReply) _lockedAction", _lockedAction)
81
+ console.log("(DirCaptureUserReply) _lockedIntent", _lockedIntent)
82
+ callback();
83
+ return;
84
+ } else {
85
+ try {
86
+ await this.chatbot.unlockIntent(this.requestId);
87
+ await this.chatbot.unlockAction(this.requestId);
88
+ console.log("unlo")
89
+ }
90
+ catch(e) {
91
+ console.error("Erro", e)
92
+ }
93
+
94
+ }
95
+ try {
96
+ const user_reply = this.message.text;
97
+ if (this.context.tdcache) {
98
+ if (action.assignResultTo) {
99
+ if (this.log) {console.log("assign assignResultTo:", action.assignResultTo);}
100
+ await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, action.assignResultTo, user_reply);
101
+ }
102
+ }
103
+
104
+ if (callback) {
105
+ console.log("(DirCaptureUserReply) #executeGoTo(goToIntent)", goToIntent)
106
+ this.#executeGoTo(goToIntent, () => {
107
+ callback(); // continue the flow
108
+ });
109
+ }
110
+ }
111
+ catch(error) {
112
+ console.error("error is", error);
113
+ }
114
+ }
115
+
116
+ #executeGoTo(intent, callback) {
117
+ let goToIntentDirective = null;
118
+ if (intent) {
119
+ goToIntentDirective = DirIntent.intentDirectiveFor(intent);
120
+ }
121
+ this.intentDir.execute(goToIntentDirective, () => {
122
+ callback();
123
+ });
124
+ }
125
+
126
+ }
127
+
128
+ module.exports = { DirCaptureUserReply };
@@ -32,24 +32,75 @@ class DirDepartment {
32
32
 
33
33
  }
34
34
 
35
- // execute(requestId, dep_name, callback) {
36
- // if (this.log) {console.log("DirDepartment:", dep_name);}
37
- // this.moveToDepartment(requestId, dep_name, () => {
38
- // callback();
39
- // });
35
+ // example dept
36
+ // {
37
+ // "routing": "assigned",
38
+ // "default": false,
39
+ // "status": 0,
40
+ // "_id": "65204737f8c0cf002cf41a60",
41
+ // "name": "dep2",
42
+ // "id_project": "65203e12f8c0cf002cf4110b",
43
+ // "createdBy": "5e09d16d4d36110017506d7f",
44
+ // "tags": [],
45
+ // "createdAt": "2023-10-06T17:43:19.991Z",
46
+ // "updatedAt": "2023-10-07T15:28:31.775Z",
47
+ // "__v": 0,
48
+ // "id_bot": "65204767f8c0cf002cf41ada",
49
+ // "id_group": null,
50
+ // "hasBot": true,
51
+ // "id": "65204737f8c0cf002cf41a60"
40
52
  // }
41
53
 
42
54
  go(action, callback) {
43
- console.log("Switching to department:", action.depName);
44
- this.moveToDepartment(this.requestId, action.depName, () => {
45
- console.log("Switched, callbackalling");
46
- callback();
55
+ if (this.log) {console.log("Switching to department:", action.depName);}
56
+ const depName = action.depName;
57
+ this.moveToDepartment(this.requestId, depName, (deps) => {
58
+ if (!deps) {
59
+ if (this.log) {console.log("Dep not found");}
60
+ callback();
61
+ return
62
+ }
63
+ if (this.log) {console.log("Switched to dept:", depName, "action:", JSON.stringify(action));}
64
+ if (action.triggerBot) {
65
+ let dep = null;
66
+ let i;
67
+ for (i = 0; i < deps.length; i++) {
68
+ let d = deps[i];
69
+ if (d.name.toLowerCase() === depName.toLowerCase()) {
70
+ dep = d;
71
+ break;
72
+ }
73
+ }
74
+ if (dep && dep.hasBot === true && dep.id_bot) {
75
+ if (this.log) {console.log("Sending hidden /start message to bot in dept");}
76
+ const message = {
77
+ type: "text",
78
+ text: "/start",
79
+ attributes : {
80
+ subtype: "info"
81
+ }
82
+ }
83
+ this.tdclient.sendSupportMessage(
84
+ this.requestId,
85
+ message, (err) => {
86
+ if (err) {
87
+ console.error("Error sending hidden message:", err.message);
88
+ }
89
+ if (this.log) {console.log("Hidden message sent.");}
90
+ callback();
91
+ });
92
+ }
93
+ }
94
+ else {
95
+ if (this.log) {console.log("No action.triggerBot");}
96
+ callback();
97
+ }
47
98
  });
48
99
  }
49
100
 
50
101
  moveToDepartment(requestId, depName, callback) {
51
102
  this.tdclient.getAllDepartments((err, deps) => {
52
- if (this.log) {console.log("deps:", deps);}
103
+ if (this.log) {console.log("deps:", JSON.stringify(deps));}
53
104
  if (err) {
54
105
  console.error("getAllDepartments() error:", err);
55
106
  callback();
@@ -72,7 +123,7 @@ class DirDepartment {
72
123
  }
73
124
  else {
74
125
  console.log("DirDepartment response:",JSON.stringify(res));
75
- callback();
126
+ callback(deps);
76
127
  }
77
128
  });
78
129
  }
@@ -1,8 +1,7 @@
1
1
  const { Filler } = require('../Filler');
2
2
  const { TiledeskChatbot } = require('../../models/TiledeskChatbot');
3
3
  const { DirIntent } = require('./DirIntent');
4
- const { DirLockIntent } = require('../tiledeskChatbotPlugs/directives/DirLockIntent');
5
- const { DirUnlockIntent } = require('../tiledeskChatbotPlugs/directives/DirUnlockIntent');
4
+ const { IntentForm } = require('../../models/IntentForm.js');
6
5
 
7
6
  class DirForm {
8
7
  constructor(context) {
@@ -11,6 +10,7 @@ class DirForm {
11
10
  }
12
11
  this.context = context;
13
12
  this.tdclient = context.tdclient;
13
+ this.chatbot = context.chatbot;
14
14
  this.tdcache = context.tdcache;
15
15
  this.requestId = context.requestId;
16
16
  this.intentDir = new DirIntent(context);
@@ -28,18 +28,17 @@ class DirForm {
28
28
  return;
29
29
  }
30
30
  this.go(action, (stop) => {
31
- if (this.log) {console.log("(webrequestv2, stop?", stop); }
31
+ if (this.log) {console.log("(DirForm, stop?", stop); }
32
32
  callback(stop);
33
33
  });
34
34
  }
35
35
 
36
36
  async go(action, callback) {
37
- let intent_name = answerObj.intent_display_name
38
37
  // THE FORM
39
38
  // if (intent_name === "test_form_intent") {
40
- // answerObj.form = {
39
+ // action.form = {
41
40
  // "cancelCommands": ['reset', 'cancel'],
42
- // "cancelReply": "Ok canceled!",
41
+ // "cancelReply": "Ok canceled!", // REMOVE
43
42
  // "fields": [
44
43
  // {
45
44
  // "name": "userFullname",
@@ -60,15 +59,17 @@ class DirForm {
60
59
  // ]
61
60
  // };
62
61
  // }
63
- let intent_form = answerObj.form;
62
+ const trueIntent = action.trueIntent; // edit-end (success)
63
+ const falseIntent = action.falseIntent; // cancel
64
+ let form = action.form;
64
65
  if (this.log) {
65
- console.log("IntentForm.isValidForm(intent_form)", IntentForm.isValidForm(intent_form));
66
+ console.log("IntentForm.isValidForm(intent_form)", IntentForm.isValidForm(form));
66
67
  }
67
68
  let clientUpdateUserFullname = null;
68
- if (IntentForm.isValidForm(intent_form)) {
69
- await this.lockIntent(this.requestId, intent_name);
69
+ if (IntentForm.isValidForm(form)) {
70
+ await this.chatbot.lockAction(this.requestId, action.action_id);
70
71
  const user_reply = message.text;
71
- let form_reply = await this.execIntentForm(user_reply, intent_form);
72
+ let form_reply = await this.execIntentForm(user_reply, form);
72
73
  // console.log("got form reply", form_reply)
73
74
  if (!form_reply.canceled && form_reply.message) {
74
75
  // console.log("Form replying for next field...");
@@ -80,7 +81,18 @@ class DirForm {
80
81
  form_reply.message.attributes.fillParams = true;
81
82
  form_reply.message.attributes.splits = true;
82
83
  form_reply.message.attributes.markbot = true;
83
- return form_reply.message;
84
+ // return form_reply.message;
85
+
86
+ this.context.tdclient.sendSupportMessage(
87
+ this.requestId,
88
+ form_reply.message,
89
+ (err) => {
90
+ if (err) {
91
+ console.error("Error sending form reply:", err.message);
92
+ }
93
+ if (this.log) {console.log("Form reply message sent.");}
94
+ callback(true);
95
+ });
84
96
  }
85
97
  else if (form_reply.end) {
86
98
  if (this.log) {
@@ -88,43 +100,85 @@ class DirForm {
88
100
  console.log("unlocking intent for request:", this.requestId);
89
101
  console.log("populate data on lead:", JSON.stringify(lead));
90
102
  }
91
- this.unlockIntent(this.requestId);
92
- if (lead) {
93
- this.populatePrechatFormAndLead(lead._id, this.requestId);
94
- }
95
- else {
96
- if (this.log) {console.log("No lead. Skipping populatePrechatFormAndLead()");}
97
- }
98
- const all_parameters = await this.allParameters();
99
- // if (this.log) {console.log("We have all_parameters:", all_parameters)};
100
- if (all_parameters && all_parameters["userFullname"]) {
101
- clientUpdateUserFullname = all_parameters["userFullname"];
103
+ this.chatbot.unlockAction(this.requestId);
104
+
105
+ if (callback) {
106
+ this.#executeCondition(true, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
107
+ callback(false); // continue the flow
108
+ });
102
109
  }
110
+ // TODO: INVOKE DIR_INTENT FOR END-FORM (SUCCESS)
111
+ // if (lead) {
112
+ // this.populatePrechatFormAndLead(lead._id, this.requestId);
113
+ // }
114
+ // else {
115
+ // if (this.log) {console.log("No lead. Skipping populatePrechatFormAndLead()");}
116
+ // }
117
+ // const all_parameters = await this.chatbot.allParameters();
118
+ // if (all_parameters && all_parameters["userFullname"]) {
119
+ // clientUpdateUserFullname = all_parameters["userFullname"];
120
+ // }
103
121
  }
104
122
  else if (form_reply.canceled) {
105
- console.log("Form canceled.");
106
123
  if (this.log) {console.log("unlocking intent due to canceling, for request", this.requestId);}
107
- this.unlockIntent(this.requestId);
108
- if (this.log) {console.log("sending form 'cancel' reply...", form_reply.message)}
109
- // reply with this message (ex. please enter your fullname)
110
- if (!form_reply.message.attributes) {
111
- form_reply.message.attributes = {}
124
+ this.unlockAction(this.requestId);
125
+
126
+ // TODO: INVOKE DIR_INTENT FOR CANCEL.
127
+ if (callback) {
128
+ this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
129
+ callback(false); // continue the flow
130
+ });
112
131
  }
113
- form_reply.message.attributes.fillParams = true;
114
- form_reply.message.attributes.splits = true;
115
- form_reply.message.attributes.directives = true;
132
+
133
+ // if (this.log) {console.log("sending form 'cancel' reply...", form_reply.message)}
134
+ // TODO: REMOVE CANCEL REPLY
135
+ // reply with this message (ex. please enter your fullname)
136
+ // if (!form_reply.message.attributes) {
137
+ // form_reply.message.attributes = {}
138
+ // }
139
+ // form_reply.message.attributes.fillParams = true;
140
+ // form_reply.message.attributes.splits = true;
141
+ // form_reply.message.attributes.directives = true;
116
142
  // // used by the Clients to get some info about the intent that generated this reply
117
143
  // form_reply.message.attributes.intent_display_name = faq.intent_display_name;
118
144
  // form_reply.message.attributes.intent_id = faq.intent_id;
119
- return form_reply.message
145
+ // return form_reply.message
120
146
  }
121
147
  }
122
148
  // FORM END
123
149
  }
124
150
 
125
- async lockIntent(requestId, intent_name) {
126
- // await this.tdcache.set("tilebot:requests:" + requestId + ":locked", intent_name);
127
- await DirLockIntent.lockIntent(this.tdcache, requestId, intent_name);
151
+ async #executeCondition(result, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, callback) {
152
+ let trueIntentDirective = null;
153
+ if (trueIntent) {
154
+ trueIntentDirective = DirIntent.intentDirectiveFor(trueIntent, trueIntentAttributes);
155
+ }
156
+ let falseIntentDirective = null;
157
+ if (falseIntent) {
158
+ falseIntentDirective = DirIntent.intentDirectiveFor(falseIntent, falseIntentAttributes);
159
+ }
160
+ if (result === true) {
161
+ if (trueIntentDirective) {
162
+ this.intentDir.execute(trueIntentDirective, () => {
163
+ callback();
164
+ });
165
+ }
166
+ else {
167
+ if (this.log) {console.log("No trueIntentDirective specified");}
168
+ callback();
169
+ }
170
+ }
171
+ else {
172
+ if (falseIntentDirective) {
173
+ this.intentDir.execute(falseIntentDirective, () => {
174
+ callback();
175
+ });
176
+ }
177
+ else {
178
+ if (this.log) {console.log("No falseIntentDirective specified");}
179
+ callback();
180
+ }
181
+ }
128
182
  }
129
183
 
130
184
  }
@@ -122,7 +122,7 @@ class DirJSONCondition {
122
122
  if (falseIntentDirective) {
123
123
  this.intentDir.execute(falseIntentDirective, () => {
124
124
  // console.log("result === false. stopOnConditionMet?", stopOnConditionMet);
125
- callback(stopOnConditionMet); // TODO should be always be false!
125
+ callback(stopOnConditionMet);
126
126
  });
127
127
  }
128
128
  else {
@@ -24,12 +24,13 @@ class DirMessage {
24
24
  this.projectId = context.projectId;
25
25
  this.requestId = context.requestId;
26
26
  this.token = context.token;
27
+ this.log = this.context.log;
27
28
  }
28
29
 
29
30
  execute(directive, callback) {
30
31
  let action;
31
32
  if (directive.action) {
32
- // console.log("got action:", JSON.stringify(action));
33
+ if (this.log) {console.log("got action:", JSON.stringify(action));}
33
34
  action = directive.action;
34
35
  if (!action.attributes) {
35
36
  action.attributes = {}
@@ -47,7 +47,7 @@ class DirReply {
47
47
  if (this.log) {
48
48
  for (const [key, value] of Object.entries(requestAttributes)) {
49
49
  const value_type = typeof value;
50
- if (this.log) {console.log("(DirReply) request parameter:", key, "value:", value, "type:", value_type)}
50
+ // if (this.log) {console.log("(DirReply) request parameter:", key, "value:", value, "type:", value_type)}
51
51
  }
52
52
  }
53
53
  const filler = new Filler();
@@ -233,11 +233,34 @@ class DirWebRequestV2 {
233
233
  })
234
234
  .catch( (err) => {
235
235
  if (this.log) {
236
- console.error("An error occurred:", JSON.stringify(err));
236
+ // FIX THE STRINGIFY OF CIRCULAR STRUCTURE BUG - START
237
+ let cache = [];
238
+ let error_log = JSON.stringify(err, function(key, value) { // try to use a separate function
239
+ if (typeof value === 'object' && value != null) {
240
+ if (cache.indexOf(value) !== -1) {
241
+ return;
242
+ }
243
+ cache.push(value);
244
+ }
245
+ return value;
246
+ });
247
+ console.error("An error occurred: ", error_log);
248
+ // FIX THE STRINGIFY OF CIRCULAR STRUCTURE BUG - END
249
+ // console.error("An error occurred:", JSON.stringify(err));
237
250
  }
238
251
  if (callback) {
239
252
  let status = 1000;
240
- let error = JSON.parse( JSON.stringify(err)) // "status" disappears without this trick
253
+ let cache = [];
254
+ let str_error = JSON.stringify(err, function(key, value) { // try to use a separate function
255
+ if (typeof value === 'object' && value != null) {
256
+ if (cache.indexOf(value) !== -1) {
257
+ return;
258
+ }
259
+ cache.push(value);
260
+ }
261
+ return value;
262
+ });
263
+ let error = JSON.parse(str_error) // "status" disappears without this trick
241
264
  let errorMessage = JSON.stringify(error);
242
265
  if (error.status) {
243
266
  status = error.status;
@@ -255,6 +278,7 @@ class DirWebRequestV2 {
255
278
  }
256
279
  });
257
280
  }
281
+
258
282
  }
259
283
 
260
284
  module.exports = { DirWebRequestV2 };
@@ -36,7 +36,8 @@ class DirWhatsappByAttribute {
36
36
  }
37
37
 
38
38
  if (process.env.API_URL) {
39
- whatsapp_api_url = process.env.API_URL + "/modules/whatsapp";
39
+ whatsapp_api_url = "https://tiledesk-whatsapp-app-pre.giovannitroisi3.repl.co/api";
40
+ // whatsapp_api_url = process.env.API_URL + "/modules/whatsapp";
40
41
  console.log("(Tilebot) DirWhatsappByAttribute whatsapp_api_url: ", whatsapp_api_url);
41
42
  } else {
42
43
  console.error("(Tilebot) ERROR Missing whatsapp_api_url. Unable to use action WhatsApp By Attributes");
@@ -28,6 +28,8 @@ class Directives {
28
28
  static WHATSAPP_ATTRIBUTE = 'whatsapp_attribute';
29
29
  static ASK_GPT = "askgpt";
30
30
  static GPT_TASK = "gpt_task";
31
+ static FORM = "form";
32
+ static CAPTURE_USER_REPLY = "capture_user_reply";
31
33
 
32
34
  // static WHEN_ONLINE_MOVE_TO_AGENT = "whenonlinemovetoagent"; // DEPRECATED?
33
35
  // static WHEN_OFFLINE_HOURS = "whenofflinehours"; // DEPRECATED // adds a message on top of the original message when offline hours opts: --replace