@ibti-tech/chatbot 0.6.2 → 0.8.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/README.md +68 -566
- package/dist/index.d.ts +333 -29
- package/dist/index.mjs +467 -106
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -59,6 +59,38 @@ var dispatchAssitantAnswer = /* @__PURE__ */ __name(() => {
|
|
|
59
59
|
document.dispatchEvent(evt);
|
|
60
60
|
}, "dispatchAssitantAnswer");
|
|
61
61
|
|
|
62
|
+
// src/services/api/http-client.ts
|
|
63
|
+
var getDefaultHeaders = /* @__PURE__ */ __name(() => ({
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
"User-Agent": typeof navigator !== "undefined" ? navigator.userAgent : "IBTI-Chatbot/1.0"
|
|
66
|
+
}), "getDefaultHeaders");
|
|
67
|
+
|
|
68
|
+
// src/services/chatbot/chatbot.api.ts
|
|
69
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
70
|
+
var cache = /* @__PURE__ */ new Map();
|
|
71
|
+
function cacheKey(apiURL, publicHash) {
|
|
72
|
+
const base = apiURL.replace(/\/+$/, "");
|
|
73
|
+
return `${base}:${publicHash}`;
|
|
74
|
+
}
|
|
75
|
+
__name(cacheKey, "cacheKey");
|
|
76
|
+
var getChatbotByPublicHash = /* @__PURE__ */ __name(async (apiURL, publicHash) => {
|
|
77
|
+
const key = cacheKey(apiURL, publicHash);
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const entry = cache.get(key);
|
|
80
|
+
if (entry && entry.expires > now) return entry.data;
|
|
81
|
+
const response = await fetch(`${apiURL}/chatbots/public/${publicHash}`, {
|
|
82
|
+
method: "GET",
|
|
83
|
+
headers: getDefaultHeaders(),
|
|
84
|
+
cache: "default"
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`Chatbot not found (${response.status})`);
|
|
88
|
+
}
|
|
89
|
+
const data = await response.json();
|
|
90
|
+
cache.set(key, { data, expires: now + CACHE_TTL_MS });
|
|
91
|
+
return data;
|
|
92
|
+
}, "getChatbotByPublicHash");
|
|
93
|
+
|
|
62
94
|
// src/i18n/en.json
|
|
63
95
|
var en_default = {
|
|
64
96
|
CHATBOT_NAME: "IBTI Chatbot (Beta)",
|
|
@@ -71,7 +103,9 @@ var en_default = {
|
|
|
71
103
|
},
|
|
72
104
|
WRITING_MESSAGE: "Writing",
|
|
73
105
|
ERRORS: {
|
|
74
|
-
UNKNOWN: "Sorry. We couldn't provide a response."
|
|
106
|
+
UNKNOWN: "Sorry. We couldn't provide a response.",
|
|
107
|
+
DOMAIN_NOT_ALLOWED: "The chatbot is not available on this domain.",
|
|
108
|
+
CHATBOT_NOT_FOUND: "Chatbot not found. Please check the configuration."
|
|
75
109
|
},
|
|
76
110
|
INPUT_PLACEHOLDER: "Ask something",
|
|
77
111
|
CHATBOT_BAR: {
|
|
@@ -110,7 +144,9 @@ var pt_BR_default = {
|
|
|
110
144
|
},
|
|
111
145
|
WRITING_MESSAGE: "Escrevendo",
|
|
112
146
|
ERRORS: {
|
|
113
|
-
UNKNOWN: "Desculpe. N\xE3o conseguimos fornecer uma resposta."
|
|
147
|
+
UNKNOWN: "Desculpe. N\xE3o conseguimos fornecer uma resposta.",
|
|
148
|
+
DOMAIN_NOT_ALLOWED: "O chatbot n\xE3o est\xE1 dispon\xEDvel neste dom\xEDnio.",
|
|
149
|
+
CHATBOT_NOT_FOUND: "Chatbot n\xE3o encontrado. Verifique a configura\xE7\xE3o."
|
|
114
150
|
},
|
|
115
151
|
INPUT_PLACEHOLDER: "Pergunte alguma coisa",
|
|
116
152
|
CHATBOT_BAR: {
|
|
@@ -143,115 +179,137 @@ var i18n = {
|
|
|
143
179
|
"pt-BR": pt_BR_default
|
|
144
180
|
};
|
|
145
181
|
|
|
146
|
-
// src/services/
|
|
182
|
+
// src/services/chat/chat.api.ts
|
|
147
183
|
var sendChatContext = /* @__PURE__ */ __name(async ({
|
|
148
184
|
chatContext,
|
|
149
185
|
locale = "en",
|
|
150
186
|
onReceiving,
|
|
151
187
|
onDone,
|
|
152
|
-
apiURL
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
188
|
+
apiURL,
|
|
189
|
+
publicHash,
|
|
190
|
+
visitorId
|
|
191
|
+
}) => {
|
|
192
|
+
const params = new URLSearchParams({ locale });
|
|
193
|
+
if (publicHash) params.set("hash", publicHash);
|
|
194
|
+
if (visitorId) params.set("visitorId", visitorId);
|
|
195
|
+
const response = await fetch(
|
|
196
|
+
`${apiURL}/chat/completion?${params.toString()}`,
|
|
197
|
+
{
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: getDefaultHeaders(),
|
|
200
|
+
body: JSON.stringify({ context: chatContext })
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
if (!response.ok) {
|
|
163
204
|
throw new Error(
|
|
164
205
|
i18n[locale].ERRORS.UNKNOWN + ` (status: ${response.status})`
|
|
165
206
|
);
|
|
207
|
+
}
|
|
166
208
|
if (!response.body) return;
|
|
167
209
|
const reader = response.body.getReader();
|
|
168
210
|
const decoder = new TextDecoder();
|
|
169
211
|
let message = "";
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
apiURL,
|
|
187
|
-
chatContext,
|
|
188
|
-
locale = "en",
|
|
189
|
-
ratingScore,
|
|
190
|
-
description
|
|
191
|
-
}) => fetch(`${apiURL}/chat/feedback`, {
|
|
192
|
-
method: "POST",
|
|
193
|
-
headers: {
|
|
194
|
-
"Content-type": "application/json"
|
|
195
|
-
},
|
|
196
|
-
body: JSON.stringify({
|
|
197
|
-
locale,
|
|
198
|
-
rating_score: ratingScore,
|
|
199
|
-
chat_context: chatContext,
|
|
200
|
-
message: description
|
|
201
|
-
})
|
|
202
|
-
}), "sendUserChatFeedback");
|
|
203
|
-
var checkApiHealth = /* @__PURE__ */ __name(async (apiUrl, locale = "en") => {
|
|
212
|
+
const read = /* @__PURE__ */ __name(() => reader.read().then(({ done, value }) => {
|
|
213
|
+
if (done) {
|
|
214
|
+
onDone?.();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
218
|
+
message += chunk;
|
|
219
|
+
onReceiving(message, chunk);
|
|
220
|
+
return read();
|
|
221
|
+
}), "read");
|
|
222
|
+
return read();
|
|
223
|
+
}, "sendChatContext");
|
|
224
|
+
var checkApiHealth = /* @__PURE__ */ __name(async (apiUrl, locale = "en", publicHash) => {
|
|
225
|
+
const params = new URLSearchParams({ locale });
|
|
226
|
+
if (publicHash) params.set("hash", publicHash);
|
|
227
|
+
const url = `${apiUrl}/chat/welcome?${params.toString()}`;
|
|
204
228
|
const controller = new AbortController();
|
|
205
|
-
const
|
|
229
|
+
const timeoutMs = 5e3;
|
|
230
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
206
231
|
try {
|
|
207
|
-
const
|
|
232
|
+
const response = await fetch(url, {
|
|
208
233
|
method: "HEAD",
|
|
209
|
-
|
|
210
|
-
|
|
234
|
+
signal: controller.signal,
|
|
235
|
+
headers: getDefaultHeaders()
|
|
211
236
|
});
|
|
212
237
|
clearTimeout(timeoutId);
|
|
213
|
-
return
|
|
214
|
-
} catch
|
|
238
|
+
return response.ok;
|
|
239
|
+
} catch {
|
|
215
240
|
clearTimeout(timeoutId);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
241
|
+
}
|
|
242
|
+
const fallbackController = new AbortController();
|
|
243
|
+
const fallbackTimeoutId = setTimeout(
|
|
244
|
+
() => fallbackController.abort(),
|
|
245
|
+
timeoutMs
|
|
246
|
+
);
|
|
247
|
+
try {
|
|
248
|
+
const response = await fetch(url, {
|
|
249
|
+
method: "GET",
|
|
250
|
+
signal: fallbackController.signal,
|
|
251
|
+
headers: getDefaultHeaders()
|
|
252
|
+
});
|
|
253
|
+
clearTimeout(fallbackTimeoutId);
|
|
254
|
+
return response.ok;
|
|
255
|
+
} catch {
|
|
256
|
+
clearTimeout(fallbackTimeoutId);
|
|
257
|
+
return false;
|
|
232
258
|
}
|
|
233
259
|
}, "checkApiHealth");
|
|
234
|
-
var getWelcomeMessage = /* @__PURE__ */ __name(async (apiUrl, locale = "en") => {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
260
|
+
var getWelcomeMessage = /* @__PURE__ */ __name(async (apiUrl, locale = "en", publicHash) => {
|
|
261
|
+
const params = new URLSearchParams({ locale });
|
|
262
|
+
if (publicHash) params.set("hash", publicHash);
|
|
263
|
+
const response = await fetch(
|
|
264
|
+
`${apiUrl}/chat/welcome?${params.toString()}`,
|
|
265
|
+
{
|
|
266
|
+
method: "GET",
|
|
267
|
+
headers: getDefaultHeaders()
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
if (!response.ok) {
|
|
239
271
|
throw new Error(
|
|
240
|
-
i18n[locale].ERRORS.UNKNOWN + ` (status: ${
|
|
272
|
+
i18n[locale].ERRORS.UNKNOWN + ` (status: ${response.status})`
|
|
241
273
|
);
|
|
242
274
|
}
|
|
243
|
-
const data = await
|
|
275
|
+
const data = await response.json();
|
|
244
276
|
return data.welcomeMessage;
|
|
245
277
|
}, "getWelcomeMessage");
|
|
246
278
|
|
|
279
|
+
// src/services/feedback/feedback.api.ts
|
|
280
|
+
var sendUserChatFeedback = /* @__PURE__ */ __name(async ({
|
|
281
|
+
apiURL,
|
|
282
|
+
chatContext,
|
|
283
|
+
locale = "en",
|
|
284
|
+
ratingScore,
|
|
285
|
+
description,
|
|
286
|
+
publicHash,
|
|
287
|
+
visitorId
|
|
288
|
+
}) => {
|
|
289
|
+
return fetch(`${apiURL}/chat/feedback`, {
|
|
290
|
+
method: "POST",
|
|
291
|
+
headers: getDefaultHeaders(),
|
|
292
|
+
body: JSON.stringify({
|
|
293
|
+
locale,
|
|
294
|
+
rating_score: ratingScore,
|
|
295
|
+
chat_context: chatContext,
|
|
296
|
+
message: description,
|
|
297
|
+
...publicHash && { hash: publicHash },
|
|
298
|
+
...visitorId && { visitorId }
|
|
299
|
+
})
|
|
300
|
+
});
|
|
301
|
+
}, "sendUserChatFeedback");
|
|
302
|
+
|
|
247
303
|
// src/cookies/chat.tsx
|
|
248
304
|
import { parseCookies, setCookie } from "nookies";
|
|
249
305
|
var ChatCookieManager = class {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.cookieKey = "ibtiChatbot@chat";
|
|
253
|
-
// Keep for migration purposes
|
|
306
|
+
// Keep for migration purposes
|
|
307
|
+
constructor(keySuffix) {
|
|
254
308
|
this.maxAgeInHours = 24;
|
|
309
|
+
const base = "ibtiChatbot@chat";
|
|
310
|
+
const suffix = keySuffix ? `:${keySuffix}` : "";
|
|
311
|
+
this.storageKey = `${base}${suffix}`;
|
|
312
|
+
this.cookieKey = `${base}${suffix}`;
|
|
255
313
|
}
|
|
256
314
|
static {
|
|
257
315
|
__name(this, "ChatCookieManager");
|
|
@@ -420,10 +478,13 @@ var useI18n = /* @__PURE__ */ __name(() => {
|
|
|
420
478
|
// src/contexts/Chatbot/useChatbotMessages.ts
|
|
421
479
|
var useChatbotMessages = /* @__PURE__ */ __name(({
|
|
422
480
|
apiURL,
|
|
423
|
-
locale
|
|
481
|
+
locale,
|
|
482
|
+
publicHash,
|
|
483
|
+
visitorId
|
|
424
484
|
}) => {
|
|
425
485
|
const i18n2 = useI18n();
|
|
426
|
-
const
|
|
486
|
+
const cookieKeySuffix = publicHash && apiURL ? `${apiURL.replace(/\/+$/, "")}:${publicHash}` : void 0;
|
|
487
|
+
const chatCookie = new ChatCookieManager(cookieKeySuffix);
|
|
427
488
|
const [chatMessages, setChatMessages] = useState2(
|
|
428
489
|
[]
|
|
429
490
|
);
|
|
@@ -525,6 +586,8 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
|
|
|
525
586
|
await sendChatContext({
|
|
526
587
|
apiURL,
|
|
527
588
|
locale,
|
|
589
|
+
publicHash,
|
|
590
|
+
visitorId,
|
|
528
591
|
chatContext: [
|
|
529
592
|
...chatMessages.map((item) => ({
|
|
530
593
|
role: item.role,
|
|
@@ -596,8 +659,11 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
|
|
|
596
659
|
};
|
|
597
660
|
}, []);
|
|
598
661
|
const init = /* @__PURE__ */ __name(async () => {
|
|
662
|
+
if (publicHash !== void 0 && visitorId === void 0) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
599
665
|
try {
|
|
600
|
-
const isApiAvailable = await checkApiHealth(apiURL, locale);
|
|
666
|
+
const isApiAvailable = await checkApiHealth(apiURL, locale, publicHash);
|
|
601
667
|
if (!isApiAvailable) {
|
|
602
668
|
setApiConnectionError(true);
|
|
603
669
|
setInitialized(true);
|
|
@@ -616,7 +682,7 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
|
|
|
616
682
|
setInitialized(true);
|
|
617
683
|
} else {
|
|
618
684
|
try {
|
|
619
|
-
const firstMessage = await getWelcomeMessage(apiURL, locale);
|
|
685
|
+
const firstMessage = await getWelcomeMessage(apiURL, locale, publicHash);
|
|
620
686
|
setChatMessages([
|
|
621
687
|
{
|
|
622
688
|
id: uuidv4(),
|
|
@@ -639,8 +705,9 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
|
|
|
639
705
|
}, "init");
|
|
640
706
|
useEffect(() => {
|
|
641
707
|
if (initialized) return;
|
|
708
|
+
if (publicHash !== void 0 && visitorId === void 0) return;
|
|
642
709
|
init();
|
|
643
|
-
}, [initialized]);
|
|
710
|
+
}, [initialized, publicHash, visitorId]);
|
|
644
711
|
return {
|
|
645
712
|
chatMessages,
|
|
646
713
|
makeQuestion,
|
|
@@ -659,17 +726,156 @@ var themes = {
|
|
|
659
726
|
light: defaultLightTheme,
|
|
660
727
|
dark: defaultDarkTheme
|
|
661
728
|
};
|
|
729
|
+
function mergeThemeWithCustomColors(baseTheme, customColors) {
|
|
730
|
+
const has = /* @__PURE__ */ __name((v) => v && v.trim().length > 0, "has");
|
|
731
|
+
if (!customColors || !Object.values(customColors).some(has)) {
|
|
732
|
+
return baseTheme;
|
|
733
|
+
}
|
|
734
|
+
const primary = has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
|
|
735
|
+
const headerBg = has(customColors.headerBackground) ? customColors.headerBackground : has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
|
|
736
|
+
const userBalloon = has(customColors.userBalloonColor) ? customColors.userBalloonColor : has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
|
|
737
|
+
return {
|
|
738
|
+
...baseTheme,
|
|
739
|
+
colors: {
|
|
740
|
+
...baseTheme.colors,
|
|
741
|
+
palette: {
|
|
742
|
+
...baseTheme.colors.palette,
|
|
743
|
+
primary: {
|
|
744
|
+
...baseTheme.colors.palette.primary,
|
|
745
|
+
normal: primary
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
custom: {
|
|
749
|
+
headerBackground: headerBg,
|
|
750
|
+
userBalloonColor: userBalloon
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
__name(mergeThemeWithCustomColors, "mergeThemeWithCustomColors");
|
|
662
756
|
|
|
663
757
|
// src/contexts/Chatbot/provider.tsx
|
|
664
758
|
import { ThemeProvider } from "styled-components";
|
|
759
|
+
|
|
760
|
+
// src/utils/domainValidation.ts
|
|
761
|
+
var ALLOWED_ORIGINS_WITHOUT_DOMAIN_CHECK = [
|
|
762
|
+
"localhost",
|
|
763
|
+
"127.0.0.1",
|
|
764
|
+
"null",
|
|
765
|
+
"https://admin-chatbot.ibti.tech",
|
|
766
|
+
"https://admin-chatbot.ibti.tech/",
|
|
767
|
+
"http://admin-chatbot.ibti.tech",
|
|
768
|
+
"http://admin-chatbot.ibti.tech/"
|
|
769
|
+
];
|
|
770
|
+
function normalizeHost(host) {
|
|
771
|
+
const lower = host.toLowerCase().trim();
|
|
772
|
+
if (lower.startsWith("localhost") || lower.startsWith("127.0.0.1")) {
|
|
773
|
+
return lower.split(":")[0] ?? lower;
|
|
774
|
+
}
|
|
775
|
+
return lower;
|
|
776
|
+
}
|
|
777
|
+
__name(normalizeHost, "normalizeHost");
|
|
778
|
+
function normalizeDomainName(domainName) {
|
|
779
|
+
return domainName.toLowerCase().trim();
|
|
780
|
+
}
|
|
781
|
+
__name(normalizeDomainName, "normalizeDomainName");
|
|
782
|
+
function getHostname(value) {
|
|
783
|
+
const trimmed = value.toLowerCase().trim();
|
|
784
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
785
|
+
try {
|
|
786
|
+
return new URL(trimmed).hostname;
|
|
787
|
+
} catch {
|
|
788
|
+
return trimmed.split(":")[0] ?? trimmed;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return trimmed.split(":")[0] ?? trimmed;
|
|
792
|
+
}
|
|
793
|
+
__name(getHostname, "getHostname");
|
|
794
|
+
function isOriginAllowedWithoutCheck(originOrHost, customAllowedOrigins) {
|
|
795
|
+
const list = customAllowedOrigins ?? ALLOWED_ORIGINS_WITHOUT_DOMAIN_CHECK;
|
|
796
|
+
const normalized = originOrHost.toLowerCase().trim().replace(/\/+$/, "");
|
|
797
|
+
const hostname = getHostname(originOrHost);
|
|
798
|
+
return list.some((allowed) => {
|
|
799
|
+
const a = allowed.toLowerCase().trim().replace(/\/+$/, "");
|
|
800
|
+
const aHostname = getHostname(a);
|
|
801
|
+
if (normalized === a || hostname === aHostname) return true;
|
|
802
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
803
|
+
return aHostname === "localhost" || aHostname === "127.0.0.1";
|
|
804
|
+
}
|
|
805
|
+
return false;
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
__name(isOriginAllowedWithoutCheck, "isOriginAllowedWithoutCheck");
|
|
809
|
+
function isDomainAllowed(currentOriginOrHost, domainName, customAllowedOrigins) {
|
|
810
|
+
const origin = currentOriginOrHost ?? "";
|
|
811
|
+
if (!origin || origin === "null") return true;
|
|
812
|
+
if (isOriginAllowedWithoutCheck(origin, customAllowedOrigins)) {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
const current = normalizeHost(
|
|
816
|
+
origin.startsWith("http") ? new URL(origin).host : origin
|
|
817
|
+
);
|
|
818
|
+
const allowed = normalizeDomainName(domainName);
|
|
819
|
+
if (current === allowed) return true;
|
|
820
|
+
const currentHostname = current.split(":")[0] ?? current;
|
|
821
|
+
if (currentHostname === allowed) return true;
|
|
822
|
+
if (currentHostname.endsWith("." + allowed)) return true;
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
__name(isDomainAllowed, "isDomainAllowed");
|
|
826
|
+
|
|
827
|
+
// src/utils/visitorId.ts
|
|
828
|
+
import { v4 as uuidv42 } from "uuid";
|
|
829
|
+
var STORAGE_PREFIX = "ibtiChatbot@visitorId";
|
|
830
|
+
function storageKey(apiURL, publicHash) {
|
|
831
|
+
const base = apiURL.replace(/\/+$/, "");
|
|
832
|
+
return `${STORAGE_PREFIX}:${base}:${publicHash}`;
|
|
833
|
+
}
|
|
834
|
+
__name(storageKey, "storageKey");
|
|
835
|
+
function getOrCreateVisitorId(apiURL, publicHash) {
|
|
836
|
+
if (typeof window === "undefined") {
|
|
837
|
+
return uuidv42();
|
|
838
|
+
}
|
|
839
|
+
const key = storageKey(apiURL, publicHash);
|
|
840
|
+
const existing = localStorage.getItem(key);
|
|
841
|
+
if (existing) {
|
|
842
|
+
return existing;
|
|
843
|
+
}
|
|
844
|
+
const newId = uuidv42();
|
|
845
|
+
localStorage.setItem(key, newId);
|
|
846
|
+
return newId;
|
|
847
|
+
}
|
|
848
|
+
__name(getOrCreateVisitorId, "getOrCreateVisitorId");
|
|
849
|
+
|
|
850
|
+
// src/contexts/Chatbot/provider.tsx
|
|
665
851
|
var ChatbotProvider = /* @__PURE__ */ __name(({
|
|
666
852
|
locale,
|
|
667
853
|
children,
|
|
668
854
|
apiURL,
|
|
855
|
+
publicHash,
|
|
669
856
|
theme,
|
|
670
857
|
isOpen = false,
|
|
671
|
-
texts
|
|
858
|
+
texts,
|
|
859
|
+
allowedOriginsWithoutCheck,
|
|
860
|
+
skipDomainCheck,
|
|
861
|
+
colors: customColors,
|
|
862
|
+
icon: customIcon
|
|
672
863
|
}) => {
|
|
864
|
+
const [initializationStatus, setInitializationStatus] = useState3(publicHash ? "loading" : "ready");
|
|
865
|
+
const [chatbot, setChatbot] = useState3();
|
|
866
|
+
const [domainAllowed, setDomainAllowed] = useState3(!publicHash);
|
|
867
|
+
const [visitorId, setVisitorId] = useState3(
|
|
868
|
+
() => publicHash ? void 0 : void 0
|
|
869
|
+
);
|
|
870
|
+
const isReady = initializationStatus === "ready" && (domainAllowed || !publicHash);
|
|
871
|
+
useEffect2(() => {
|
|
872
|
+
if (isReady && skipDomainCheck && typeof window !== "undefined" && window.self !== window.top) {
|
|
873
|
+
try {
|
|
874
|
+
window.parent.postMessage({ type: "IBTI_CHATBOT_READY" }, "*");
|
|
875
|
+
} catch {
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}, [isReady, skipDomainCheck]);
|
|
673
879
|
const {
|
|
674
880
|
makeQuestion,
|
|
675
881
|
chatMessages,
|
|
@@ -680,13 +886,26 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
|
|
|
680
886
|
suggestedQuestions
|
|
681
887
|
} = useChatbotMessages_default({
|
|
682
888
|
apiURL,
|
|
683
|
-
locale
|
|
889
|
+
locale,
|
|
890
|
+
publicHash: isReady ? publicHash : void 0,
|
|
891
|
+
visitorId: isReady ? visitorId : void 0
|
|
684
892
|
});
|
|
685
893
|
const [opened, setOpened] = useState3(isOpen);
|
|
686
894
|
const scrollRef = useRef2(null);
|
|
687
895
|
useEffect2(() => {
|
|
688
896
|
setOpened(isOpen);
|
|
689
897
|
}, [isOpen]);
|
|
898
|
+
useEffect2(() => {
|
|
899
|
+
if (skipDomainCheck && typeof window !== "undefined" && window.self !== window.top) {
|
|
900
|
+
try {
|
|
901
|
+
window.parent.postMessage(
|
|
902
|
+
{ type: "IBTI_CHATBOT_OPEN_STATE", isOpen: opened },
|
|
903
|
+
"*"
|
|
904
|
+
);
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}, [opened, skipDomainCheck]);
|
|
690
909
|
const openedToggle = /* @__PURE__ */ __name(() => {
|
|
691
910
|
setOpened((state) => !state);
|
|
692
911
|
}, "openedToggle");
|
|
@@ -696,6 +915,59 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
|
|
|
696
915
|
if (loading) return "loading";
|
|
697
916
|
return "online";
|
|
698
917
|
}, [writing, loading, error, apiConnectionError]);
|
|
918
|
+
useEffect2(() => {
|
|
919
|
+
if (!publicHash) {
|
|
920
|
+
setInitializationStatus("ready");
|
|
921
|
+
setDomainAllowed(true);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
let cancelled = false;
|
|
925
|
+
const bootstrap = /* @__PURE__ */ __name(async () => {
|
|
926
|
+
try {
|
|
927
|
+
const data = await getChatbotByPublicHash(apiURL, publicHash);
|
|
928
|
+
if (cancelled) return;
|
|
929
|
+
if (skipDomainCheck) {
|
|
930
|
+
setChatbot(data);
|
|
931
|
+
setDomainAllowed(true);
|
|
932
|
+
const vid = getOrCreateVisitorId(apiURL, publicHash);
|
|
933
|
+
setVisitorId(vid);
|
|
934
|
+
setInitializationStatus("ready");
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
let currentOrigin = typeof window !== "undefined" ? window.location.origin : "";
|
|
938
|
+
if (typeof window !== "undefined" && (currentOrigin === "null" || currentOrigin === "" || currentOrigin == null)) {
|
|
939
|
+
setChatbot(data);
|
|
940
|
+
setDomainAllowed(true);
|
|
941
|
+
const vid = getOrCreateVisitorId(apiURL, publicHash);
|
|
942
|
+
setVisitorId(vid);
|
|
943
|
+
setInitializationStatus("ready");
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const allowed = isDomainAllowed(
|
|
947
|
+
currentOrigin,
|
|
948
|
+
data.domain.name,
|
|
949
|
+
allowedOriginsWithoutCheck
|
|
950
|
+
);
|
|
951
|
+
setChatbot(data);
|
|
952
|
+
setDomainAllowed(allowed);
|
|
953
|
+
if (allowed) {
|
|
954
|
+
const vid = getOrCreateVisitorId(apiURL, publicHash);
|
|
955
|
+
setVisitorId(vid);
|
|
956
|
+
setInitializationStatus("ready");
|
|
957
|
+
} else {
|
|
958
|
+
setInitializationStatus("domain_not_allowed");
|
|
959
|
+
}
|
|
960
|
+
} catch (err) {
|
|
961
|
+
if (cancelled) return;
|
|
962
|
+
setInitializationStatus("error");
|
|
963
|
+
setDomainAllowed(false);
|
|
964
|
+
}
|
|
965
|
+
}, "bootstrap");
|
|
966
|
+
bootstrap();
|
|
967
|
+
return () => {
|
|
968
|
+
cancelled = true;
|
|
969
|
+
};
|
|
970
|
+
}, [apiURL, publicHash, skipDomainCheck, allowedOriginsWithoutCheck]);
|
|
699
971
|
return /* @__PURE__ */ React3.createElement(
|
|
700
972
|
ChatbotContext.Provider,
|
|
701
973
|
{
|
|
@@ -713,10 +985,16 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
|
|
|
713
985
|
suggestedQuestions,
|
|
714
986
|
apiURL,
|
|
715
987
|
texts,
|
|
716
|
-
theme
|
|
988
|
+
theme,
|
|
989
|
+
icon: customIcon,
|
|
990
|
+
publicHash,
|
|
991
|
+
visitorId,
|
|
992
|
+
domainAllowed,
|
|
993
|
+
initializationStatus,
|
|
994
|
+
chatbot
|
|
717
995
|
}
|
|
718
996
|
},
|
|
719
|
-
/* @__PURE__ */ React3.createElement(ThemeProvider, { theme: themes[theme] }, children)
|
|
997
|
+
/* @__PURE__ */ React3.createElement(ThemeProvider, { theme: mergeThemeWithCustomColors(themes[theme], customColors) }, children)
|
|
720
998
|
);
|
|
721
999
|
}, "ChatbotProvider");
|
|
722
1000
|
|
|
@@ -959,7 +1237,7 @@ var ProfileImage = styled2.span`
|
|
|
959
1237
|
}
|
|
960
1238
|
`;
|
|
961
1239
|
var Wrapper2 = styled2.div`
|
|
962
|
-
background: ${(props) => props.theme.colors.layers[2].background};
|
|
1240
|
+
background: ${(props) => props.theme.colors.custom?.headerBackground ?? props.theme.colors.layers[2].background};
|
|
963
1241
|
border: 1px solid ${(props) => props.theme.colors.layers[2].border};
|
|
964
1242
|
display: flex;
|
|
965
1243
|
padding: ${(props) => props.theme.spacing.components.small};
|
|
@@ -978,14 +1256,49 @@ var Wrapper2 = styled2.div`
|
|
|
978
1256
|
|
|
979
1257
|
// src/components/BotIcon/index.tsx
|
|
980
1258
|
import React4 from "react";
|
|
1259
|
+
|
|
1260
|
+
// src/components/BotIcon/embedIcons.tsx
|
|
1261
|
+
import {
|
|
1262
|
+
Bot,
|
|
1263
|
+
MessageCircle,
|
|
1264
|
+
MessageSquare,
|
|
1265
|
+
MessageSquarePlus,
|
|
1266
|
+
MessageSquareDashed,
|
|
1267
|
+
Sparkles,
|
|
1268
|
+
Headphones,
|
|
1269
|
+
HelpCircle
|
|
1270
|
+
} from "lucide-react";
|
|
1271
|
+
var EMBED_ICON_MAP = {
|
|
1272
|
+
Bot,
|
|
1273
|
+
MessageCircle,
|
|
1274
|
+
MessageSquare,
|
|
1275
|
+
MessageSquarePlus,
|
|
1276
|
+
MessageSquareDashed,
|
|
1277
|
+
Sparkles,
|
|
1278
|
+
Headphones,
|
|
1279
|
+
HelpCircle
|
|
1280
|
+
};
|
|
1281
|
+
function getEmbedIconComponent(name) {
|
|
1282
|
+
if (!name) return null;
|
|
1283
|
+
return EMBED_ICON_MAP[name] ?? null;
|
|
1284
|
+
}
|
|
1285
|
+
__name(getEmbedIconComponent, "getEmbedIconComponent");
|
|
1286
|
+
|
|
1287
|
+
// src/components/BotIcon/index.tsx
|
|
1288
|
+
var DefaultBotSvg = /* @__PURE__ */ __name(() => /* @__PURE__ */ React4.createElement("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em" }, /* @__PURE__ */ React4.createElement(
|
|
1289
|
+
"path",
|
|
1290
|
+
{
|
|
1291
|
+
d: "M17.7530511,13.999921 C18.9956918,13.999921 20.0030511,15.0072804 20.0030511,16.249921 L20.0030511,17.1550008 C20.0030511,18.2486786 19.5255957,19.2878579 18.6957793,20.0002733 C17.1303315,21.344244 14.8899962,22.0010712 12,22.0010712 C9.11050247,22.0010712 6.87168436,21.3444691 5.30881727,20.0007885 C4.48019625,19.2883988 4.00354153,18.2500002 4.00354153,17.1572408 L4.00354153,16.249921 C4.00354153,15.0072804 5.01090084,13.999921 6.25354153,13.999921 L17.7530511,13.999921 Z M11.8985607,2.00734093 L12.0003312,2.00049432 C12.380027,2.00049432 12.6938222,2.2826482 12.7434846,2.64872376 L12.7503312,2.75049432 L12.7495415,3.49949432 L16.25,3.5 C17.4926407,3.5 18.5,4.50735931 18.5,5.75 L18.5,10.254591 C18.5,11.4972317 17.4926407,12.504591 16.25,12.504591 L7.75,12.504591 C6.50735931,12.504591 5.5,11.4972317 5.5,10.254591 L5.5,5.75 C5.5,4.50735931 6.50735931,3.5 7.75,3.5 L11.2495415,3.49949432 L11.2503312,2.75049432 C11.2503312,2.37079855 11.5324851,2.05700336 11.8985607,2.00734093 L12.0003312,2.00049432 L11.8985607,2.00734093 Z M9.74928905,6.5 C9.05932576,6.5 8.5,7.05932576 8.5,7.74928905 C8.5,8.43925235 9.05932576,8.99857811 9.74928905,8.99857811 C10.4392523,8.99857811 10.9985781,8.43925235 10.9985781,7.74928905 C10.9985781,7.05932576 10.4392523,6.5 9.74928905,6.5 Z M14.2420255,6.5 C13.5520622,6.5 12.9927364,7.05932576 12.9927364,7.74928905 C12.9927364,8.43925235 13.5520622,8.99857811 14.2420255,8.99857811 C14.9319888,8.99857811 15.4913145,8.43925235 15.4913145,7.74928905 C15.4913145,7.05932576 14.9319888,6.5 14.2420255,6.5 Z",
|
|
1292
|
+
fill: "currentColor"
|
|
1293
|
+
}
|
|
1294
|
+
)), "DefaultBotSvg");
|
|
981
1295
|
var BotIcon = /* @__PURE__ */ __name(() => {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
));
|
|
1296
|
+
const { icon: iconName } = useChatbot_default();
|
|
1297
|
+
const IconComponent = getEmbedIconComponent(iconName);
|
|
1298
|
+
if (IconComponent) {
|
|
1299
|
+
return /* @__PURE__ */ React4.createElement(IconComponent, { size: 20, strokeWidth: 1.8 });
|
|
1300
|
+
}
|
|
1301
|
+
return /* @__PURE__ */ React4.createElement(DefaultBotSvg, null);
|
|
989
1302
|
}, "BotIcon");
|
|
990
1303
|
|
|
991
1304
|
// src/hooks/useChatbotTexts.ts
|
|
@@ -1306,7 +1619,7 @@ var Balloon = styled5.div`
|
|
|
1306
1619
|
`;
|
|
1307
1620
|
case "sent":
|
|
1308
1621
|
return css2`
|
|
1309
|
-
background: ${(props) => props.theme.colors.palette.primary.normal};
|
|
1622
|
+
background: ${(props) => props.theme.colors.custom?.userBalloonColor ?? props.theme.colors.palette.primary.normal};
|
|
1310
1623
|
color: white;
|
|
1311
1624
|
border-top-right-radius: 0;
|
|
1312
1625
|
text-align: left;
|
|
@@ -1314,18 +1627,27 @@ var Balloon = styled5.div`
|
|
|
1314
1627
|
&::after {
|
|
1315
1628
|
border-width: 0px 0px 10px 10px;
|
|
1316
1629
|
border-color: transparent transparent transparent
|
|
1317
|
-
${(props) => props.theme.colors.palette.primary.normal};
|
|
1630
|
+
${(props) => props.theme.colors.custom?.userBalloonColor ?? props.theme.colors.palette.primary.normal};
|
|
1318
1631
|
top: 0;
|
|
1319
1632
|
right: -10px;
|
|
1320
1633
|
}
|
|
1321
1634
|
`;
|
|
1322
1635
|
}
|
|
1323
1636
|
}}
|
|
1324
|
-
|
|
1325
|
-
|
|
1637
|
+
|
|
1326
1638
|
display: flex;
|
|
1327
|
-
|
|
1328
|
-
|
|
1639
|
+
${({ $error }) => $error ? css2`
|
|
1640
|
+
flex-direction: row;
|
|
1641
|
+
align-items: center;
|
|
1642
|
+
gap: ${(props) => props.theme.spacing.components.small};
|
|
1643
|
+
|
|
1644
|
+
& > *:first-of-type {
|
|
1645
|
+
flex-shrink: 0;
|
|
1646
|
+
}
|
|
1647
|
+
` : css2`
|
|
1648
|
+
flex-direction: column;
|
|
1649
|
+
gap: 1.3em;
|
|
1650
|
+
`}
|
|
1329
1651
|
|
|
1330
1652
|
p {
|
|
1331
1653
|
margin: 0;
|
|
@@ -1955,10 +2277,20 @@ var SuggestedQuestions_default = SuggestedQuestions;
|
|
|
1955
2277
|
|
|
1956
2278
|
// src/components/ChatbotBody/index.tsx
|
|
1957
2279
|
var ChatbotBody = /* @__PURE__ */ __name(({}) => {
|
|
1958
|
-
const {
|
|
2280
|
+
const {
|
|
2281
|
+
chatMessages,
|
|
2282
|
+
suggestedQuestions,
|
|
2283
|
+
loading,
|
|
2284
|
+
initializationStatus,
|
|
2285
|
+
domainAllowed
|
|
2286
|
+
} = useChatbot();
|
|
2287
|
+
const i18n2 = useI18n();
|
|
1959
2288
|
const scrollableElementRef = useRef5(null);
|
|
1960
2289
|
const { scrollProgress, scrollToEnd } = useElementScroll(scrollableElementRef);
|
|
1961
2290
|
const { dateUTC } = useUpdatedTime({ intervalInSeconds: 15 });
|
|
2291
|
+
const showDomainError = !domainAllowed && initializationStatus === "domain_not_allowed";
|
|
2292
|
+
const showInitError = initializationStatus === "error";
|
|
2293
|
+
const showLoading = initializationStatus === "loading";
|
|
1962
2294
|
useEffect6(() => {
|
|
1963
2295
|
if (scrollProgress > 85) scrollToEnd();
|
|
1964
2296
|
document.addEventListener(
|
|
@@ -1977,6 +2309,26 @@ var ChatbotBody = /* @__PURE__ */ __name(({}) => {
|
|
|
1977
2309
|
scrollToEnd();
|
|
1978
2310
|
}
|
|
1979
2311
|
}, [loading, scrollToEnd]);
|
|
2312
|
+
if (showLoading) {
|
|
2313
|
+
return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, /* @__PURE__ */ React10.createElement(WritingIndicator_default, null)));
|
|
2314
|
+
}
|
|
2315
|
+
if (showDomainError || showInitError) {
|
|
2316
|
+
const errorMessage = showDomainError ? i18n2.ERRORS.DOMAIN_NOT_ALLOWED ?? "O chatbot n\xE3o est\xE1 dispon\xEDvel neste dom\xEDnio." : i18n2.ERRORS.CHATBOT_NOT_FOUND ?? "Chatbot n\xE3o encontrado. Verifique a configura\xE7\xE3o.";
|
|
2317
|
+
return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, /* @__PURE__ */ React10.createElement(
|
|
2318
|
+
MessageBalloon_default,
|
|
2319
|
+
{
|
|
2320
|
+
key: "init-error",
|
|
2321
|
+
data: {
|
|
2322
|
+
id: "init-error",
|
|
2323
|
+
timestamp: (/* @__PURE__ */ new Date()).toUTCString(),
|
|
2324
|
+
role: "assistant",
|
|
2325
|
+
content: errorMessage,
|
|
2326
|
+
error: true
|
|
2327
|
+
},
|
|
2328
|
+
currentDateUTC: dateUTC
|
|
2329
|
+
}
|
|
2330
|
+
)));
|
|
2331
|
+
}
|
|
1980
2332
|
return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, chatMessages.map((msgData) => /* @__PURE__ */ React10.createElement(
|
|
1981
2333
|
MessageBalloon_default,
|
|
1982
2334
|
{
|
|
@@ -2353,11 +2705,16 @@ var Title2 = styled10.span`
|
|
|
2353
2705
|
`;
|
|
2354
2706
|
var Box = styled10(CardBase)`
|
|
2355
2707
|
width: 100%;
|
|
2708
|
+
max-width: 100%;
|
|
2356
2709
|
height: max-content;
|
|
2710
|
+
max-height: 100%;
|
|
2711
|
+
min-height: 0;
|
|
2712
|
+
overflow-y: auto;
|
|
2357
2713
|
padding: ${(props) => props.theme.spacing.components.medium};
|
|
2358
2714
|
transition: ${(props) => props.theme.transitionDurations.default};
|
|
2359
2715
|
font-family: 'Montserrat', sans-serif !important;
|
|
2360
2716
|
font-weight: 600 !important;
|
|
2717
|
+
box-sizing: border-box;
|
|
2361
2718
|
|
|
2362
2719
|
* {
|
|
2363
2720
|
font-family: 'Montserrat', sans-serif !important;
|
|
@@ -2378,6 +2735,8 @@ var Wrapper9 = styled10.div`
|
|
|
2378
2735
|
align-items: center;
|
|
2379
2736
|
justify-content: center;
|
|
2380
2737
|
padding: ${(props) => props.theme.spacing.components.medium};
|
|
2738
|
+
box-sizing: border-box;
|
|
2739
|
+
overflow: auto;
|
|
2381
2740
|
transition: ${(props) => props.theme.transitionDurations.default};
|
|
2382
2741
|
|
|
2383
2742
|
${(props) => props.$opened ? css5`
|
|
@@ -2448,7 +2807,7 @@ var ChatUserFeedbackRating = /* @__PURE__ */ __name(({
|
|
|
2448
2807
|
// src/contexts/Chatbot/useChatFeedbackBox.ts
|
|
2449
2808
|
import { useState as useState9 } from "react";
|
|
2450
2809
|
var useChatFeedbackBox = /* @__PURE__ */ __name(() => {
|
|
2451
|
-
const { chatMessages, apiURL, locale } = useChatbot_default();
|
|
2810
|
+
const { chatMessages, apiURL, locale, publicHash, visitorId } = useChatbot_default();
|
|
2452
2811
|
const [rated, setRated] = useState9(false);
|
|
2453
2812
|
const [opened, setOpened] = useState9(false);
|
|
2454
2813
|
const [loading, setLoading] = useState9(false);
|
|
@@ -2467,6 +2826,8 @@ var useChatFeedbackBox = /* @__PURE__ */ __name(() => {
|
|
|
2467
2826
|
setLoading(true);
|
|
2468
2827
|
await sendUserChatFeedback({
|
|
2469
2828
|
apiURL,
|
|
2829
|
+
publicHash,
|
|
2830
|
+
visitorId,
|
|
2470
2831
|
chatContext: chatMessages.map((item) => ({
|
|
2471
2832
|
role: item.role,
|
|
2472
2833
|
content: item.content
|