@tiledesk/tiledesk-tybot-connector 2.1.2 → 2.1.5

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.
@@ -0,0 +1,154 @@
1
+ const { DirIntent } = require('./DirIntent');
2
+ const { DirJSONCondition } = require('./DirJSONCondition');
3
+ const { TiledeskChatbot } = require('../../engine/TiledeskChatbot');
4
+ const { TiledeskWhenExpression } = require('../../TiledeskWhenExpression');
5
+ const winston = require('../../utils/winston');
6
+ const { Logger } = require('../../Logger');
7
+
8
+ /**
9
+ * DirJSONConditionV2
10
+ *
11
+ * Evaluates JSON conditions using the new `when` infix expression field, via the
12
+ * safe (no-eval / no-vm2) TiledeskWhenExpression engine.
13
+ *
14
+ * Backward compatibility: if the action has NO `when` field, this directive
15
+ * delegates to the legacy DirJSONCondition (left unchanged), so existing bots
16
+ * keep working exactly as before.
17
+ *
18
+ * The callback contract matches the dispatcher (DirectivesChatbotPlug): the
19
+ * callback is invoked with `stop` (truthy halts the directive chain) and is
20
+ * guaranteed to be called EXACTLY ONCE on every path, including errors.
21
+ */
22
+ class DirJSONConditionV2 {
23
+
24
+ constructor(context) {
25
+ if (!context) {
26
+ throw new Error('context object is mandatory.');
27
+ }
28
+ this.context = context;
29
+ this.chatbot = context.chatbot;
30
+ this.requestId = this.context.requestId;
31
+
32
+ this.intentDir = new DirIntent(context);
33
+ this.logger = new Logger({ request_id: this.requestId, dev: this.context.supportRequest?.draft, intent_id: this.context.reply?.intent_id || this.context.reply?.attributes?.intent_info?.intent_id });
34
+ }
35
+
36
+ execute(directive, callback) {
37
+ winston.verbose("Execute JSONConditionV2 directive");
38
+ let action;
39
+ if (directive.action) {
40
+ action = directive.action;
41
+ }
42
+ else {
43
+ this.logger.error("Incorrect action for ", directive.name, directive);
44
+ winston.warn("DirJSONConditionV2 Incorrect directive: ", directive);
45
+ callback();
46
+ return;
47
+ }
48
+
49
+ // Backward compatibility: without a `when` field, fall back to the legacy engine.
50
+ const when = action.when;
51
+ if (typeof when !== 'string' || when.trim() === "") {
52
+ winston.verbose("(DirJSONConditionV2) No 'when' field, delegating to legacy DirJSONCondition");
53
+ new DirJSONCondition(this.context).execute(directive, callback);
54
+ return;
55
+ }
56
+
57
+ this.go(action, (stop) => {
58
+ this.logger.native("[ConditionV2] Executed");
59
+ callback(stop);
60
+ }).catch((err) => {
61
+ // Last-resort net: never leave the directive chain hanging.
62
+ winston.error("(DirJSONConditionV2) Unhandled error in go(): " + (err && err.message));
63
+ callback();
64
+ });
65
+ }
66
+
67
+ async go(action, callback) {
68
+ // Guarantee the callback fires exactly once, regardless of which branch/throw occurs.
69
+ let finished = false;
70
+ const done = (stop) => {
71
+ if (finished) return;
72
+ finished = true;
73
+ callback(stop);
74
+ };
75
+
76
+ try {
77
+ const when = action.when;
78
+ let trueIntent = action.trueIntent;
79
+ let falseIntent = action.falseIntent;
80
+ const trueIntentAttributes = action.trueIntentAttributes;
81
+ const falseIntentAttributes = action.falseIntentAttributes;
82
+ // Decision: keep legacy behavior — always stop after a condition is met.
83
+ const stopOnConditionMet = true; // action.stopOnConditionMet not honored yet (known limitation)
84
+
85
+ if (trueIntent && trueIntent.trim() === "") trueIntent = null;
86
+ if (falseIntent && falseIntent.trim() === "") falseIntent = null;
87
+
88
+ if (!trueIntent && !falseIntent) {
89
+ this.logger.warn("[ConditionV2] Invalid jsonCondition, no intents specified");
90
+ winston.warn("(DirJSONConditionV2) Invalid jsonCondition, no intents specified");
91
+ done();
92
+ return;
93
+ }
94
+
95
+ const trueIntentDirective = trueIntent ? DirIntent.intentDirectiveFor(trueIntent, trueIntentAttributes) : null;
96
+ const falseIntentDirective = falseIntent ? DirIntent.intentDirectiveFor(falseIntent, falseIntentAttributes) : null;
97
+
98
+ // Load conversation variables (native-typed) from cache.
99
+ let variables = {};
100
+ if (this.context.tdcache) {
101
+ variables = await TiledeskChatbot.allParametersStatic(this.context.tdcache, this.context.requestId) || {};
102
+ }
103
+ else {
104
+ winston.error("(DirJSONConditionV2) No this.context.tdcache — evaluating with empty variables");
105
+ }
106
+
107
+ // Evaluate the `when` expression safely. Engine errors -> result === null.
108
+ let result;
109
+ try {
110
+ const value = new TiledeskWhenExpression().evaluate(when, variables);
111
+ result = (value === null || value === undefined) ? null : Boolean(value);
112
+ }
113
+ catch (err) {
114
+ winston.error("(DirJSONConditionV2) Error evaluating 'when' expression: " + (err && err.message));
115
+ result = null;
116
+ }
117
+ this.logger.native("[ConditionV2] Evaluated 'when' result: " + result);
118
+ winston.debug("(DirJSONConditionV2) Evaluation result: " + result);
119
+
120
+ if (result === true) {
121
+ if (trueIntentDirective) {
122
+ this.intentDir.execute(trueIntentDirective, () => done(stopOnConditionMet));
123
+ }
124
+ else {
125
+ this.logger.native("[ConditionV2] No trueIntentDirective specified");
126
+ done();
127
+ }
128
+ return;
129
+ }
130
+
131
+ // result === false OR result === null (evaluation error)
132
+ if (result === null) {
133
+ this.logger.error("[ConditionV2] An error occurred evaluating the condition");
134
+ if (this.context.tdcache) {
135
+ await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, "flowError", "An error occurred evaluating condition (when)");
136
+ }
137
+ }
138
+ if (falseIntentDirective) {
139
+ this.intentDir.execute(falseIntentDirective, () => done(stopOnConditionMet));
140
+ }
141
+ else {
142
+ this.logger.native("[ConditionV2] No falseIntentDirective specified");
143
+ done();
144
+ }
145
+ }
146
+ catch (err) {
147
+ winston.error("(DirJSONConditionV2) Unexpected error in go(): " + (err && err.message));
148
+ done(); // advance the chain rather than hang
149
+ }
150
+ }
151
+
152
+ }
153
+
154
+ module.exports = { DirJSONConditionV2 };
@@ -36,16 +36,19 @@ class DirMoveToAgent {
36
36
  winston.error("DirMoveToAgent) Error moving to agent: ", err);
37
37
  }
38
38
  else {
39
- // Successfully moved to agent
40
- AnalyticsClient.track('handover_to_human', this.context.projectId, {
41
- id_request: this.requestId,
42
- human_id: null,
43
- reason: 'bot_directive',
44
- department_id: this.context.departmentId || null,
45
- waiting_time_seconds: null,
46
- agent_id: this.context.chatbot?.bot.root_id || this.context.chatbot?.botId,
47
- trigger_intent: this.context.reply?.attributes?.intent_info?.intent_name || null
48
- });
39
+ // Successfully moved to agent. Only track published (production) runs
40
+ // (root/draft copy has no root_id).
41
+ if (this.context.chatbot?.bot.root_id) {
42
+ AnalyticsClient.track('handover_to_human', this.context.projectId, {
43
+ id_request: this.requestId,
44
+ human_id: null,
45
+ reason: 'bot_directive',
46
+ department_id: this.context.departmentId || null,
47
+ waiting_time_seconds: null,
48
+ agent_id: this.context.chatbot?.bot.root_id,
49
+ trigger_intent: this.context.reply?.attributes?.intent_info?.intent_name || null
50
+ });
51
+ }
49
52
  }
50
53
  callback();
51
54
  });
@@ -87,13 +87,16 @@ class DirReplaceBotV2 {
87
87
 
88
88
  winston.debug("(DirReplaceBotV2) replace resbody: ", resbody)
89
89
 
90
- // Emit analytics event for bot switch
91
- AnalyticsClient.track('agent.bot_switched', this.context.projectId, {
92
- from_agent_id: this.context.chatbot?.bot.root_id || this.context.chatbot?.botId || '',
93
- to_agent_id: botName || resbody?.bot?._id || '',
94
- intent_name: this.context.reply?.attributes?.intent_info?.intent_name || null,
95
- request_id: this.requestId || null
96
- });
90
+ // Emit analytics event for bot switch. Only track published (production)
91
+ // runs (root/draft copy has no root_id).
92
+ if (this.context.chatbot?.bot.root_id) {
93
+ AnalyticsClient.track('agent.bot_switched', this.context.projectId, {
94
+ from_agent_id: this.context.chatbot?.bot.root_id,
95
+ to_agent_id: resbody?.replaced_bot_root_id || botName || '',
96
+ intent_name: this.context.reply?.attributes?.intent_info?.intent_name || null,
97
+ request_id: this.requestId || null
98
+ });
99
+ }
97
100
 
98
101
  if (blockName) {
99
102
  winston.debug("(DirReplaceBotV2) Sending hidden /start message to bot in dept");
@@ -86,13 +86,16 @@ class DirReplaceBotV3 {
86
86
 
87
87
  winston.debug("(DirReplaceBotV3) replace resbody: ", resbody);
88
88
 
89
- // Emit analytics event for bot switch
90
- AnalyticsClient.track('agent.bot_switched', this.context.projectId, {
91
- from_agent_id: this.context.chatbot?.bot.root_id || this.context.chatbot?.botId || '',
92
- to_agent_id: (useSlug ? botSlug : botId) || resbody?.bot?._id || '',
93
- intent_name: this.context.reply?.attributes?.intent_info?.intent_name || null,
94
- request_id: this.requestId || null
95
- });
89
+ // Emit analytics event for bot switch. Only track published (production)
90
+ // runs (root/draft copy has no root_id).
91
+ if (this.context.chatbot?.bot.root_id) {
92
+ AnalyticsClient.track('agent.bot_switched', this.context.projectId, {
93
+ from_agent_id: this.context.chatbot?.bot.root_id,
94
+ to_agent_id: resbody?.replaced_bot_root_id || (useSlug ? botSlug : botId) || '',
95
+ intent_name: this.context.reply?.attributes?.intent_info?.intent_name || null,
96
+ request_id: this.requestId || null
97
+ });
98
+ }
96
99
 
97
100
  if (blockName) {
98
101
  winston.debug("(DirReplaceBotV3) Sending hidden /start message to bot in dept");
@@ -21,6 +21,7 @@ class Directives {
21
21
  static IF_ONLINE_AGENTS = "ifonlineagents";
22
22
  static FUNCTION_VALUE = "functionvalue";
23
23
  static JSON_CONDITION = "jsoncondition";
24
+ static JSON_CONDITION_2 = "jsoncondition2"; // NEW: azione JSON Condition V2 del Design Studio
24
25
  static SET_ATTRIBUTE = "setattribute";
25
26
  static SET_ATTRIBUTE_V2 = "setattribute-v2";
26
27
  static REPLY = 'reply';
@@ -63,7 +64,7 @@ class Directives {
63
64
  static WEB_RESPONSE = "web_response";
64
65
  static FLOW_LOG = "flow_log";
65
66
  static ADD_KB_CONTENT = "add_kb_content";
66
-
67
+ static DATA_TABLES = "data_table";
67
68
  // static WHEN_ONLINE_MOVE_TO_AGENT = "whenonlinemovetoagent"; // DEPRECATED?
68
69
  // static WHEN_OFFLINE_HOURS = "whenofflinehours"; // DEPRECATED // adds a message on top of the original message when offline hours opts: --replace
69
70
  //static WHEN_OFFLINE_HOURS_REPLACE_MESSAGE = "whenofflinehoursreplacemessage"; // REMOVE