@tiledesk/tiledesk-tybot-connector 0.2.66-rc3 → 0.2.67

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,14 +5,18 @@
5
5
  available on:
6
6
  ▶️ https://www.npmjs.com/package/@tiledesk/tiledesk-tybot-connector
7
7
 
8
- # v0.2.66-rc3
8
+ # v0.2.67
9
+ - console.log crash fixed on noinput
10
+
11
+ # v0.2.66
9
12
  - changed updateQuotes method
10
13
  - fix missing model in updateQuote
14
+ - added ReplyV2
11
15
 
12
16
  # v0.2.66-rc1
13
17
  - fix missing model in updateQuote
14
18
 
15
- # v0.2.65
19
+ # v0.2.65 - online
16
20
  - fix OPENAI_APIKEY: removed Bearer prefix
17
21
 
18
22
  # v0.2.64
@@ -81,6 +81,7 @@ class TiledeskChatbot {
81
81
  await this.unlockIntent(this.requestId);
82
82
  await this.unlockAction(this.requestId);
83
83
  // console.log("RESET LOCKED INTENT.");
84
+ await chatbot.addParameter("userInput", true); // set userInput
84
85
  if (this.log) {console.log("RESET LOCKED INTENT. Intent was explicitly invoked with an action:", message.attributes.action);}
85
86
  }
86
87
  } catch(error) {
@@ -336,6 +336,51 @@ class TiledeskChatbotUtil {
336
336
  }
337
337
  }
338
338
 
339
+ static allReplyButtons(message) {
340
+ let all_buttons = [];
341
+ if (message.attributes && message.attributes.commands) {
342
+ // console.log("message.attributes ok")
343
+ let commands = message.attributes.commands;
344
+ if (commands.length > 0) {
345
+ // console.log("commands ok", commands.length)
346
+ for (let i = 0; i < commands.length; i++) {
347
+ let command = commands[i];
348
+ // console.log("got command:", command)s
349
+ if (command.type === 'message' && command.message) {
350
+ if (command.message.attributes && command.message.attributes.attachment && command.message.attributes.attachment.buttons && command.message.attributes.attachment.buttons.length > 0){
351
+ // console.log("command with buttons ok:")
352
+ let buttons = command.message.attributes.attachment.buttons;
353
+
354
+ buttons.forEach(button => {
355
+ if (button.type === "action" || button.type === "text") {
356
+ // console.log("pushing button:", button);
357
+ all_buttons.push(button);
358
+ }
359
+ });
360
+ }
361
+ }
362
+ }
363
+ }
364
+ }
365
+ return all_buttons;
366
+ }
367
+
368
+ static buttonByText(text, buttons) {
369
+ if (buttons === null || text === null) {
370
+ return null;
371
+ }
372
+ let search_text = text.toLowerCase();
373
+ let selected_button = null;
374
+ for (let i = 0; i < buttons.length; i++) {
375
+ const button = buttons[i];
376
+ if (button.value !== null && button.value.toLowerCase() === search_text) {
377
+ selected_button = button;
378
+ break;
379
+ }
380
+ }
381
+ return selected_button;
382
+ }
383
+
339
384
  static async updateConversationTranscript(chatbot, message) {
340
385
  if (!message || !message.senderFullname) { // not a conversation, can it be an Automation invocation?
341
386
  return null;
@@ -451,6 +496,7 @@ class TiledeskChatbotUtil {
451
496
  }
452
497
 
453
498
  if (message.text && message.sender !== "_tdinternal") {
499
+ await chatbot.addParameter("userInput", true); // set userInput
454
500
  await chatbot.addParameter(TiledeskChatbotConst.REQ_LAST_USER_TEXT_KEY, message.text); // DEPRECATED
455
501
  await chatbot.addParameter(TiledeskChatbotConst.REQ_LAST_USER_TEXT_v2_KEY, message.text);
456
502
  if (message.channel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-tybot-connector",
3
- "version": "0.2.66-rc3",
3
+ "version": "0.2.67",
4
4
  "description": "Tiledesk Tybot connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -47,6 +47,7 @@ const { DirCustomerio } = require('./directives/DirCustomerio');
47
47
  const { DirBrevo } = require('./directives/DirBrevo');
48
48
  const { DirAskGPTV2 } = require('./directives/DirAskGPTV2');
49
49
  const { DirAssistant } = require('./directives/DirAssistant');
50
+ const { DirReplyV2 } = require('./directives/DirReplyV2');
50
51
 
51
52
  class DirectivesChatbotPlug {
52
53
 
@@ -293,6 +294,19 @@ class DirectivesChatbotPlug {
293
294
  this.process(next_dir);
294
295
  });
295
296
  }
297
+ else if (directive_name === Directives.REPLY_V2) {
298
+ // console.log("...DirReplyV2");
299
+ new DirReplyV2(context).execute(directive, async (stop) => {
300
+ if (stop) {
301
+ if (context.log) { console.log("Stopping Actions on:", JSON.stringify(directive));}
302
+ this.theend();
303
+ }
304
+ else {
305
+ let next_dir = await this.nextDirective(this.directives);
306
+ this.process(next_dir);
307
+ }
308
+ });
309
+ }
296
310
  else if (directive_name === Directives.DTMF_FORM) {
297
311
  // console.log("...DirReply");
298
312
  new DirReply(context).execute(directive, async () => {
@@ -125,7 +125,7 @@ class DirAssistant {
125
125
  let apikey = await this.getGPT_APIKEY();
126
126
  if (this.log) {console.log("apikey:", apikey);}
127
127
  if (!apikey) {
128
- const reply = "DirAssistant gptkey is mandatory in Integrations";
128
+ const reply = "OpenAI APIKEY is mandatory for ChatGPT Assistants. Add your personal OpenAI APIKEY in Settings > Integrations";
129
129
  if (this.log) { console.error(reply); };
130
130
  await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, assignErrorTo, reply);
131
131
  if (falseIntent) {
@@ -0,0 +1,289 @@
1
+ const { Filler } = require('../Filler');
2
+ const { TiledeskChatbot } = require('../../models/TiledeskChatbot');
3
+ const { TiledeskChatbotConst } = require('../../models/TiledeskChatbotConst');
4
+ const { TiledeskChatbotUtil } = require('../../models/TiledeskChatbotUtil');
5
+ const { DirIntent } = require("./DirIntent");
6
+
7
+ class DirReplyV2 {
8
+
9
+ constructor(context) {
10
+ if (!context) {
11
+ throw new Error('context object is mandatory.');
12
+ }
13
+ this.context = context;
14
+ this.projectId = context.projectId;
15
+ this.requestId = context.requestId;
16
+ this.token = context.token;
17
+ this.tdcache = context.tdcache;
18
+ this.log = context.log;
19
+ this.intentDir = new DirIntent(context);
20
+ this.chatbot = context.chatbot;
21
+ this.reply = context.reply;
22
+ }
23
+
24
+ execute(directive, callback) {
25
+ let action;
26
+ if (directive.action) {
27
+ action = directive.action;
28
+ if (!action.attributes) {
29
+ action.attributes = {}
30
+ }
31
+ action.attributes.fillParams = true;
32
+ }
33
+ else {
34
+ console.error("Incorrect directive (no action provided):", directive);
35
+ callback();
36
+ return;
37
+ }
38
+ this.go(action, (stop) => {
39
+ callback(stop);
40
+ });
41
+ }
42
+
43
+ async go(action, callback) {
44
+ if (this.log) { console.log("ReplyV2 action!", JSON.stringify(action)); }
45
+ const message = action;
46
+
47
+ let current; // debug only
48
+ if (this.log) {
49
+ if (message.attributes.commands[1].message.text) {
50
+ current = message.attributes.commands[1].message.text
51
+ }
52
+ console.log("current:", current);
53
+ }
54
+ let must_stop = false;
55
+ // fill
56
+ let requestAttributes = null;
57
+ if (this.tdcache) {
58
+ requestAttributes =
59
+ await TiledeskChatbot.allParametersStatic(
60
+ this.tdcache, this.requestId
61
+ );
62
+ if (this.log) {
63
+ for (const [key, value] of Object.entries(requestAttributes)) {
64
+ const value_type = typeof value;
65
+ }
66
+ }
67
+
68
+ try {
69
+ // lock/unlock + no-match
70
+ // get buttons if available
71
+ const buttons = TiledeskChatbotUtil.allReplyButtons(message);
72
+ if (this.log) { console.log("Action Buttons:", JSON.stringify(buttons)); }
73
+ if (buttons && buttons.length > 0) {
74
+ const locked = await this.lockUnlock(action); // first execution returns locked, then unlocked
75
+ if (!locked) {
76
+ if (this.log) { console.log("second pass! unlocked!"); }
77
+ const last_user_text = await this.chatbot.getParameter(TiledeskChatbotConst.REQ_LAST_USER_TEXT_v2_KEY);
78
+ if (this.log) { console.log("got last user text"); }
79
+ const button = TiledeskChatbotUtil.buttonByText(last_user_text, buttons);
80
+ if (this.log) { console.log("button found", JSON.stringify(button)); }
81
+ // invoke button
82
+ if (button && button.action) {
83
+ if (this.log) { console.log("moving to button action", button.action); }
84
+ let button_action = DirIntent.intentDirectiveFor(button.action, null);
85
+ if (this.log) { console.log("action with .intentName:", button_action); }
86
+ this.intentDir.execute(button_action, () => {
87
+ if (this.log) { console.log("action invoked", button_action); }
88
+ });
89
+ if (this.log) { console.log("callback(true) + return", current); }
90
+ callback(true); // must_stop = true
91
+ return;
92
+ }
93
+ else { // no match (treating text buttons as no-match for the moment)
94
+ // if noMatchIntent invoke
95
+ // const button = TiledeskChatbotUtil.buttonByText("nomatch", buttons);
96
+ if (this.log) { console.log("nomatch button found", JSON.stringify(button)); }
97
+ // // invoke button
98
+ // if (button && button.action) {
99
+ // console.log("moving to nomatch action", button.action);
100
+ // let button_action = DirIntent.intentDirectiveFor(button.action, null);
101
+ // this.intentDir.execute(button_action, () => {
102
+ // console.log("nomatch action invoked", button_action);
103
+ // });
104
+ // console.log("callback(true) + return 2", current);
105
+ // callback(true);
106
+ // return;
107
+ // }
108
+ if (action.noMatchIntent) {
109
+ if (this.log) { console.log("moving to nomatch action", action.noMatchIntent); }
110
+ let nomatch_action = DirIntent.intentDirectiveFor(action.noMatchIntent, null);
111
+ this.intentDir.execute(nomatch_action, () => {
112
+ if (this.log) { console.log("nomatch action invoked", nomatch_action); }
113
+ });
114
+ if (this.log) { console.log("callback(true) + return no-match", current); }
115
+ callback(true); // must_stop = true
116
+ return;
117
+ }
118
+ else {
119
+ // there is no "no-match", go on...
120
+ if (this.log) { console.log("callback(false) + return 3", current); }
121
+ callback(false);
122
+ return;
123
+ }
124
+ }
125
+ }
126
+ else {
127
+ if (this.log) { console.log("first time pass!"); }
128
+ must_stop = true; // you must stop after next callbacks (in this flow) if there are buttons
129
+ // console.log("action:", action);
130
+ if (action.noInputIntent) {
131
+ if (this.log) { console.log("NoInputIntent found:", action.noInputIntent); }
132
+ const noInputIntent = action.noInputIntent;
133
+ const noInputTimeout = action.noInputTimeout;
134
+ if (this.log) { console.log("noInputTimeout found:", noInputTimeout); }
135
+ if (noInputTimeout > 0 && noInputTimeout < 300000) {
136
+ await this.chatbot.addParameter("userInput", false); // control variable. On each user input is set to true
137
+ if (this.log) { console.log("Set userInput: false, checking...", await this.chatbot.getParameter("userInput")); }
138
+ setTimeout(async () => {
139
+ if (this.log) { console.log("noinput timeout triggered!"); }
140
+ let userInput = await this.chatbot.getParameter("userInput");
141
+ if (!userInput) {
142
+ if (this.log) { console.log("no 'userInput'. Executing noinput action:", noInputIntent); }
143
+ await this.chatbot.unlockIntent(this.requestId);
144
+ await this.chatbot.unlockAction(this.requestId);
145
+ if (this.log) { console.log("unlocked (for noInput) ReplyV2"); }
146
+ let noinput_action = DirIntent.intentDirectiveFor(noInputIntent, null);
147
+ this.intentDir.execute(noinput_action, () => {
148
+ if (this.log) { console.log("noinput action invoked", noinput_action); }
149
+ });
150
+ }
151
+ }, noInputTimeout);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ catch(error) {
158
+ console.error("Error in DirReplyV2:", error);
159
+ }
160
+
161
+
162
+ if (this.log) { console.log("proceding normally to render and send the reply", current); }
163
+ const filler = new Filler();
164
+ // fill text attribute
165
+ message.text = filler.fill(message.text, requestAttributes);
166
+ if (message.metadata) {
167
+ if (this.log) {console.log("filling message 'metadata':", JSON.stringify(message.metadata));}
168
+ if (message.metadata.src) {
169
+ message.metadata.src = filler.fill(message.metadata.src, requestAttributes);
170
+ }
171
+ if (message.metadata.name) {
172
+ message.metadata.name = filler.fill(message.metadata.name, requestAttributes);
173
+ }
174
+ }
175
+ if (this.log) {console.log("filling commands'. Message:", JSON.stringify(message));}
176
+ if (message.attributes && message.attributes.commands) {
177
+ if (this.log) {console.log("filling commands'. commands found.");}
178
+ let commands = message.attributes.commands;
179
+ if (this.log) {console.log("commands:", JSON.stringify(commands), commands.length);}
180
+ if (commands.length > 0) {
181
+ if (this.log) {console.log("commands' found");}
182
+ for (let i = 0; i < commands.length; i++) {
183
+ let command = commands[i];
184
+ if (command.type === 'message' && command.message && command.message.text) {
185
+ command.message.text = filler.fill(command.message.text, requestAttributes);
186
+ TiledeskChatbotUtil.fillCommandAttachments(command, requestAttributes, this.log);
187
+ if (this.log) {console.log("command filled:", command.message.text);}
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ // EVALUATE EXPRESSION AND REMOVE BASED ON EVALUATION
194
+ if (this.log) {console.log("message before filters:", JSON.stringify(message));}
195
+ if (message.attributes && message.attributes.commands) {
196
+ if (this.log) {console.log("filterOnVariables...on commands", JSON.stringify(message.attributes.commands));}
197
+ if (this.log) {console.log("filterOnVariables...on attributes", requestAttributes);}
198
+ // TiledeskChatbotUtil.filterOnVariables(message.attributes.commands, requestAttributes);
199
+ TiledeskChatbotUtil.filterOnVariables(message, requestAttributes);
200
+ }
201
+
202
+ // temporary send back of reserved attributes
203
+ if (!message.attributes) {
204
+ message.attributes = {}
205
+ }
206
+ // Reserved names: userEmail, userFullname
207
+ if (requestAttributes['userEmail']) {
208
+ message.attributes.updateUserEmail = requestAttributes['userEmail'];
209
+ }
210
+ if (requestAttributes['userFullname']) {
211
+ message.attributes.updateUserFullname = requestAttributes['userFullname'];
212
+ }
213
+ // intent_info
214
+ if (this.context.reply && this.context.reply.attributes && this.context.reply.attributes.intent_info) {
215
+ message.attributes.intentName = this.context.reply.attributes.intent_info.intent_name;
216
+ }
217
+ }
218
+ // send!
219
+ let cleanMessage = message;
220
+ // cleanMessage = TiledeskChatbotUtil.removeEmptyReplyCommands(message);
221
+ // if (!TiledeskChatbotUtil.isValidReply(cleanMessage)) {
222
+ // console.log("invalid message", cleanMessage);
223
+ // callback(); // cancel reply operation
224
+ // return;
225
+ // }
226
+ // console.log("valid message!", cleanMessage);
227
+ cleanMessage.senderFullname = this.context.chatbot.bot.name;
228
+ if (this.log) {console.log("Reply:", JSON.stringify(cleanMessage))};
229
+ await TiledeskChatbotUtil.updateConversationTranscript(this.context.chatbot, cleanMessage);
230
+ this.context.tdclient.sendSupportMessage(
231
+ this.requestId,
232
+ cleanMessage,
233
+ (err) => {
234
+ if (err) {
235
+ console.error("Error sending reply:", err);
236
+ }
237
+ if (this.log) {console.log("Reply message sent");}
238
+ const delay = TiledeskChatbotUtil.totalMessageWait(cleanMessage);
239
+ // console.log("got total delay:", delay)
240
+ if (delay > 0 && delay <= 30000) { // prevent long delays
241
+ if (this.log) { console.log("start timeout callback(" + must_stop + ") for:", current); }
242
+ setTimeout(async () => {
243
+ if (this.log) { console.log("callback(" + must_stop + ") after delay", current); }
244
+ callback(must_stop);
245
+ }, delay);
246
+ }
247
+ else {
248
+ // console.log("invalid delay.")
249
+ callback(must_stop);
250
+ }
251
+ });
252
+
253
+ }
254
+
255
+ async lockUnlock(action, callback) {
256
+ let lockedAction = await this.chatbot.currentLockedAction(this.requestId);
257
+ // console.log("(DirReplyV2) lockedAction:", lockedAction);
258
+ if (!lockedAction) {
259
+ // console.log("(DirReplyV2) !lockedAction");
260
+ const intent_name = this.reply.attributes.intent_info.intent_name
261
+ const actionId = action["_tdActionId"];
262
+ // console.log("(DirReplyV2) intent_name:", intent_name);
263
+ // console.log("(DirReplyV2) actionId:", actionId);
264
+ await this.chatbot.lockIntent(this.requestId, intent_name);
265
+ // console.log("(DirReplyV2) lockIntent");
266
+ await this.chatbot.lockAction(this.requestId, actionId);
267
+ // console.log("(DirReplyV2) lockAction");
268
+ let _lockedAction = await this.chatbot.currentLockedAction(this.requestId);
269
+ let _lockedIntent = await this.chatbot.currentLockedIntent(this.requestId);
270
+ // console.log("(DirReplyV2) _lockedAction", _lockedAction);
271
+ // console.log("(DirReplyV2) _lockedIntent", _lockedIntent);
272
+ // callback();
273
+ return true;
274
+ } else {
275
+ try {
276
+ await this.chatbot.unlockIntent(this.requestId);
277
+ await this.chatbot.unlockAction(this.requestId);
278
+ // console.log("unlocked ReplyV2");
279
+ return false;
280
+ }
281
+ catch(e) {
282
+ console.error("Error", e);
283
+ }
284
+ }
285
+ }
286
+
287
+ }
288
+
289
+ module.exports = { DirReplyV2 };
@@ -47,6 +47,7 @@ class Directives {
47
47
  static SPEECH_FORM = 'speech_form';
48
48
  static PLAY_PROMPT = 'play_prompt';
49
49
  static GPT_ASSISTANT = 'gpt_assistant';
50
+ static REPLY_V2 = 'replyv2';
50
51
 
51
52
  // static WHEN_ONLINE_MOVE_TO_AGENT = "whenonlinemovetoagent"; // DEPRECATED?
52
53
  // static WHEN_OFFLINE_HOURS = "whenofflinehours"; // DEPRECATED // adds a message on top of the original message when offline hours opts: --replace