Proceso de Emisión de Facturas de Venta
Sin información
SIIF Nación
Agenda
No hay eventos vigentes
Horario de atención servicio al cliente - SIIF

Teléfono
Lunes a viernes de 7:00 AM a 7:00 PM Jornada Continua En Bogotá: (601) 602 1270 opción 1 Línea Gratuita: 01-8000-910071 opción 1
Horario de atención soporte - SIIF
Lunes a Viernes de 8:00 AM a 5:00 PM Jornada Continua
Ingreso al Chat
Ingreso al Chat
Preguntas Frecuentes
Documentos Técnicos
Se ha producido un error al procesar la plantilla.
The following has evaluated to null or missing:
==> BOT_AVATAR [in template "20096#20121#2968727" at line 685, column 33]
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${BOT_AVATAR} [in template "20096#20121#2968727" at line 685, column 31]
----
1<style>
2
3 /* Works on Firefox */
4 #bot__wrapper__div * {
5 --color-principal: #3366CC;
6 scrollbar-width: thin;
7 scrollbar-color: #b1b1b1 white;
8 }
9
10 /* Works on Chrome, Edge, and Safari */
11 #bot__wrapper__div *::-webkit-scrollbar { width: 9px; }
12 #bot__wrapper__div *::-webkit-scrollbar-track { background: white; }
13 #bot__wrapper__div *::-webkit-scrollbar-thumb {
14 background-color: #b1b1b1;
15 border-radius: 5px;
16 }
17
18 .webchat__initialsAvatar--fromUser,
19 .webchat__initialsAvatar { background-color: white !important; }
20
21 .ac-pushButton.style-default.ac-selectable div { white-space: normal !important; }
22
23 #bot__wrapper__div element.style>p {
24 font-size: 14px;
25 font-weight: 400;
26 box-sizing: border-box;
27 flex: 0 0 auto;
28 margin: 0px;
29 }
30
31 #bot__wrapper__div input {
32 border-radius: 5px;
33 border-color: inherit;
34 border: 2px;
35 border-style: groove;
36 padding: 5px;
37 outline: none;
38 }
39 #bot__wrapper__div input:focus {
40 border-color: var(--color-principal);
41 border-style: solid;
42 }
43
44 #bot__wrapper__div select {
45 border-radius: 5px;
46 border-color: inherit;
47 border: 2px;
48 border-style: groove;
49 padding: 5px;
50 outline: none;
51 }
52 #bot__wrapper__div select:focus {
53 border-color: var(--color-principal);
54 border-style: solid;
55 }
56
57 #webchat { height: 100%; width: 100%; }
58 #webchat>* { font-size: 14px !important; }
59 #webchat .webchat__send-box__main button { background-color: white !important; }
60
61 .ac-textBlock p {
62 margin: 0px !important;
63 font-family: 'Work Sans', sans-serif !important;
64 }
65
66 [role=heading] p {
67 margin: 0px !important;
68 font-family: 'Montserrat', sans-serif !important;
69 color: #004884;
70 font-size: 24px;
71 }
72
73 .bext-chat-bot__wrapper {
74 width: 400px;
75 position: fixed;
76 bottom: 0;
77 right: 50px;
78 z-index: 9999;
79 border-radius: 10px;
80 box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
81 background-color: white;
82 }
83
84 .bext-chat-bot__wrapper2 {
85 position: fixed;
86 bottom: 40px;
87 right: 40px;
88 z-index: 9999;
89 }
90
91 .closelogo {
92 margin: 10px;
93 height: 100px;
94 width: auto;
95 margin-bottom: 35px;
96 padding: 0px;
97 }
98
99 .closelogo-close {
100 margin: 0px;
101 padding: 0px;
102 height: 120px;
103 width: auto;
104 }
105
106 .bext-chat-bot__title { margin: 0px; padding: 0px; }
107
108 .bext-chat-bot__noselect {
109 -webkit-touch-callout: none;
110 -webkit-user-select: none;
111 -khtml-user-select: none;
112 -moz-user-select: none;
113 -ms-user-select: none;
114 user-select: none;
115 padding: 0px;
116 margin: 0px;
117 }
118
119 .bext-chat-bot__icon { padding: 0px; margin: 0px; }
120
121 .bext-chat-bot__chat { height: 20; overflow: hidden; border-radius: 16px; }
122
123 .bext-chat-bot__iframe {
124 height: 70vh;
125 display: initial;
126 width: 100%;
127 border: none;
128 }
129
130 .bext-chat-bot__close { border: none; display: none; }
131
132 .bext-chat-bot__open { height: 510px; display: initial; width: 100%; }
133
134 .bext-chat-bot__header2 {
135 height: 105px;
136 width: 150px;
137 background-color: transparent !important;
138 display: flex;
139 border-radius: 50%;
140 flex-direction: row;
141 align-items: center;
142 justify-content: center;
143 z-index: 10;
144 position: relative;
145 padding: 0px;
146 box-shadow: none;
147 border: none;
148 }
149
150 .bext-chat-bot__header2:hover { filter: brightness(85%); cursor: pointer; }
151
152 .bext-chat-bot__header {
153 width: 100%;
154 height: 68px;
155 background-color: var(--color-principal) !important;
156 display: flex;
157 flex-direction: row;
158 align-items: center;
159 z-index: 10;
160 position: relative;
161 border-top-left-radius: 10px;
162 border-top-right-radius: 10px;
163 padding-bottom: 10px;
164 padding-top: 10px;
165 }
166
167 .bext-chat-bot__header h1,
168 .bext-chat-bot__header h2 {
169 margin: 0;
170 color: white;
171 font-size: 16px;
172 line-height: 18px !important;
173 }
174
175 .bext-chat-bot__title_header {
176 font-family: 'Montserrat', sans-serif !important;
177 font-weight: 600;
178 padding: 0px;
179 margin: 0px;
180 font-size: 23px;
181 color: white;
182 display: flex;
183 flex-direction: column;
184 align-items: baseline;
185 justify-content: center;
186 }
187
188 .gg-chevron-down {
189 box-sizing: border-box;
190 position: relative;
191 display: block;
192 transform: scale(var(--ggs, 1.3));
193 width: 22px;
194 height: 22px;
195 border: 2px solid transparent;
196 border-radius: 100px;
197 }
198 .gg-chevron-down::after {
199 content: "";
200 display: block;
201 box-sizing: border-box;
202 position: absolute;
203 width: 10px;
204 height: 10px;
205 border-bottom: 2px solid white;
206 border-right: 2px solid white;
207 transform: rotate(45deg);
208 left: 4px;
209 top: 2px;
210 }
211
212 .gg-chevron-up {
213 box-sizing: border-box;
214 position: relative;
215 display: block;
216 transform: scale(var(--ggs, 1.3));
217 width: 22px;
218 height: 22px;
219 border: 2px solid transparent;
220 border-radius: 100px;
221 }
222 .gg-chevron-up::after {
223 content: "";
224 display: block;
225 box-sizing: border-box;
226 position: absolute;
227 width: 10px;
228 height: 10px;
229 border-top: 2px solid white;
230 border-right: 2px solid white;
231 transform: rotate(-45deg);
232 left: 4px;
233 bottom: 2px;
234 }
235
236 .bext-chat-bot__arrows_container {
237 padding: 0px;
238 margin: 9px;
239 display: flex;
240 justify-content: center;
241 align-items: center;
242 }
243
244 .bext-chat-bot__arrows {
245 border-radius: 100% !important;
246 height: 50px;
247 width: 50px;
248 display: flex;
249 justify-content: center;
250 align-items: center;
251 cursor: pointer;
252 background-color: var(--color-principal);
253 transition: filter 0.3s;
254 }
255 .bext-chat-bot__arrows:hover { filter: brightness(85%); }
256
257 @media (max-width: 768px) {
258 .bext-chat-bot__wrapper {
259 width: 85%;
260 right: 30px;
261 }
262 #bext-chat-bot__iframe { width: 95%; }
263 }
264
265 @media only screen and (max-width: 468px) and (min-width: 400px) {
266 .bext-chat-bot__header { height: 90px; }
267 }
268
269 @media only screen and (max-width: 399px) {
270 .bext-chat-bot__header { height: 120px; }
271 }
272
273 body {
274 margin: 0;
275 padding: 0;
276 overflow-x: hidden;
277 }
278
279</style>
280<!--* Chatbot -->
281<div class="bext-chat-bot__wrapper" id="bot__wrapper__div">
282
283 <div class="bext-chat-bot__header" id="bext-chat-bot__btn">
284 <div class="bext-chat-bot__title bext-chat-bot__icon" padding="0px" margin="0px">
285 <a>
286 <img
287 alt="Chatbot"
288 aria-hidden="true"
289 id="bext-chat-bot__btn_img"
290 class="closelogo"
291 src="https://stonlineprodeastusbots2.blob.core.windows.net/imagenes/jarvis.png"
292 />
293 </a>
294 </div>
295
296 <div class="bext-chat-bot__title bext-chat-bot__noselect bext-chat-bot__title_header" id="header_chat">
297 <h1 style="font-size: 17px">SIIF Nación</h1>
298 <h2 style="font-size: 14px">Iris - Asistente Virtual</h2>
299 </div>
300
301 <div class="bext-chat-bot__title bext-chat-bot__arrows_container" id="bext-chat-bot__arrw">
302 <div id="bext-chat-bot__btn_collapse" class="bext-chat-bot__arrows">
303 <i id="bext-chat-bot__arrows_i" class="gg-chevron-down"></i>
304 </div>
305 </div>
306 </div>
307
308 <div id="bext-chat-bot__iframe" class="bext-chat-bot__iframe w-100" style="height: 400px;">
309
310 <div id="contenedorWeb" style="width: 97%;"></div>
311
312 <link rel="preconnect" href="https://fonts.googleapis.com" />
313 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
314 <link
315 href="https://fonts.googleapis.com/css2?family=PT+Sans:wght@400;700&display=swap"
316 rel="stylesheet"
317 />
318 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
319 <link
320 href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;600&display=swap"
321 rel="stylesheet"
322 />
323 <link
324 href="https://fonts.googleapis.com/css2?family=Montserrat:wght@600&display=swap"
325 rel="stylesheet"
326 />
327
328 <head>
329 <meta charset="UTF-8" />
330 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
331 <title>Ventana Chat Bot MINHACIENDA</title>
332
333 <style>
334 #master_chat {
335 height: 100%;
336 margin: 0;
337 }
338
339 #master_chat * {
340 --color-principal: #3366cc;
341 scrollbar-width: thin;
342 scrollbar-color: #b1b1b1 white;
343 box-sizing: border-box;
344 font-family: "Work Sans", sans-serif !important;
345 }
346
347 #webchat {
348 height: 100%;
349 width: 100%;
350 display: flex;
351 flex-direction: column;
352 background: #f5f5f5;
353 border: none;
354 }
355
356 .mh-chat__messages {
357 flex: 1;
358 overflow-y: auto;
359 padding: 14px 14px 8px 14px;
360 }
361
362 .mh-msg {
363 display: flex;
364 gap: 10px;
365 margin: 10px 0;
366 align-items: flex-end;
367 }
368
369 .mh-avatar {
370 width: 34px;
371 height: 34px;
372 border-radius: 50%;
373 background: transparent;
374 flex: 0 0 auto;
375 overflow: hidden;
376 border: 1px solid rgba(0, 0, 0, 0.08);
377 }
378
379 .mh-avatar img {
380 width: 100%;
381 height: 100%;
382 object-fit: cover;
383 display: block;
384 background: white;
385 }
386
387 .mh-bubble {
388 max-width: 78%;
389 padding: 10px 12px;
390 border-radius: 15px;
391 box-shadow: rgba(0, 0, 0, 0.06) 0 1px 2px;
392 line-height: 1.35;
393 font-size: 14px;
394 white-space: pre-wrap;
395 word-break: break-word;
396 margin: 0;
397 font-family: "Work Sans", sans-serif !important;
398 }
399
400 .mh-bubble__text {
401 display: block;
402 margin: 0;
403 padding: 0;
404 font-family: "Work Sans", sans-serif !important;
405 font-size: 14px;
406 line-height: 1.35;
407 }
408
409 .mh-bubble--bot {
410 background: #ffffff;
411 color: #111;
412 border-top-left-radius: 6px;
413 text-align: left;
414 }
415
416 .mh-bubble--user {
417 background: var(--color-principal);
418 color: #fff;
419 border-top-right-radius: 6px;
420 margin-left: auto;
421 text-align: left;
422 }
423
424 .mh-msg--user {
425 justify-content: flex-end;
426 }
427
428 .mh-bubble--loading {
429 opacity: 0.85;
430 font-style: italic;
431 font-family: "Work Sans", sans-serif !important;
432 font-size: 14px;
433 line-height: 1.35;
434 text-align: left;
435 }
436
437 .mh-chat__composer {
438 display: flex;
439 gap: 10px;
440 padding: 10px 12px;
441 background: #fff;
442 border-top: 1px solid rgba(0, 0, 0, 0.08);
443 align-items: center;
444 }
445
446 .mh-inputwrap {
447 position: relative;
448 flex: 1;
449 display: flex;
450 align-items: center;
451 min-width: 0;
452 }
453
454 .mh-input {
455 width: 100%;
456 border-radius: 10px;
457 border: 1px solid rgba(0, 0, 0, 0.18);
458 padding: 10px 44px 10px 12px;
459 outline: none;
460 font-size: 14px;
461 min-width: 0;
462 font-family: "Work Sans", sans-serif !important;
463 }
464
465 .mh-input:focus {
466 border-color: var(--color-principal);
467 box-shadow: 0 0 0 3px rgba(51, 102, 204, 0.12);
468 }
469
470 .mh-mic-in {
471 position: absolute;
472 right: 10px;
473 width: 32px;
474 height: 32px;
475 border-radius: 10px;
476 border: none;
477 background: transparent;
478 display: flex;
479 align-items: center;
480 justify-content: center;
481 cursor: pointer;
482 }
483
484 .mh-mic-in:hover {
485 background: rgba(0, 0, 0, 0.04);
486 }
487
488 .mh-mic-in:disabled {
489 opacity: 0.55;
490 cursor: not-allowed;
491 }
492
493 .mh-mic-in.is-recording svg path {
494 fill: #dc3545 !important;
495 }
496
497 .mh-send {
498 border-radius: 20px;
499 background-color: var(--color-principal);
500 border: 1px solid white;
501 color: white;
502 padding: 10px 14px;
503 font-family: "Montserrat", sans-serif !important;
504 font-weight: 600;
505 cursor: pointer;
506 transition: filter 0.2s;
507 white-space: nowrap;
508 }
509
510 .mh-send:hover {
511 filter: brightness(0.92);
512 }
513
514 .mh-send:disabled {
515 opacity: 0.6;
516 cursor: not-allowed;
517 }
518
519 .mh-welcome-card {
520 background: #fff;
521 border-radius: 16px;
522 padding: 16px;
523 box-shadow: rgba(0, 0, 0, 0.08) 0 2px 8px;
524 max-width: 85%;
525 }
526
527 .mh-welcome-title {
528 font-family: "Montserrat", sans-serif !important;
529 font-size: 26px;
530 margin: 0 0 10px 0;
531 color: #111;
532 }
533
534 .mh-welcome-text {
535 margin: 0;
536 color: #333;
537 font-size: 14px;
538 line-height: 1.45;
539 font-family: "Work Sans", sans-serif !important;
540 }
541
542 .mh-welcome-btn {
543 margin-top: 14px;
544 width: 100%;
545 border-radius: 28px;
546 background: var(--color-principal);
547 color: #fff;
548 border: none;
549 padding: 12px 14px;
550 cursor: pointer;
551 font-family: "Montserrat", sans-serif !important;
552 font-weight: 600;
553 transition: filter 0.2s;
554 }
555
556 .mh-welcome-btn:hover {
557 filter: brightness(0.92);
558 }
559
560 .mh-meta-time {
561 font-size: 12px;
562 color: #777;
563 margin-left: 44px;
564 margin-top: 4px;
565 font-family: "Work Sans", sans-serif !important;
566 }
567 </style>
568
569 <script>
570 var botServices = (function () {
571 var API_BASE = "https://minhacienda-api.proudsmoke-eaa26320.eastus.azurecontainerapps.io";
572
573 var BOT_AVATAR =
574 "https://stonlineprodeastusbots2.blob.core.windows.net/imagenes/JarvisAvatar_2.png";
575 var USER_AVATAR =
576 "https://stonlineprodeastusbots2.blob.core.windows.net/imagenes/userAvatar.png";
577
578 var WELCOME_TEXT =
579 "Bienvenido al Sistema Integrado de Información Financiera (SIIF) del Ministerio de Hacienda y Crédito Público. " +
580 "Mi nombre es Iris, y estoy aquí para ayudarte a encontrar información sobre la gestión de los recursos públicos de manera clara y accesible. " +
581 "Da clic al botón iniciar conversación";
582
583 function nowLabel() {
584 return "Ahora mismo";
585 }
586
587 function getOrCreateSessionId() {
588 var key = "mh_session_id";
589 var existing = localStorage.getItem(key);
590 if (existing && existing.trim().length > 0) return existing;
591
592 var sid =
593 "web-" +
594 Math.random().toString(16).slice(2) +
595 "-" +
596 Date.now();
597 localStorage.setItem(key, sid);
598 return sid;
599 }
600
601 function getStoredToken() {
602 return localStorage.getItem("mh_token") || "";
603 }
604
605 function setStoredToken(token) {
606 localStorage.setItem("mh_token", token);
607 }
608
609 function clearStoredToken() {
610 localStorage.removeItem("mh_token");
611 }
612
613 function getAuthHeader() {
614 var t = getStoredToken();
615 if (t && t.trim().length > 0) {
616 return { Authorization: "Bearer " + t.trim() };
617 }
618 return {};
619 }
620
621 async function requestAnonymousToken() {
622 var sessionId = getOrCreateSessionId();
623
624 var res = await fetch(API_BASE + "/api/auth/anonymous-token", {
625 method: "POST",
626 headers: {
627 "Content-Type": "application/json",
628 },
629 body: JSON.stringify({
630 session_id: sessionId,
631 }),
632 });
633
634 if (!res.ok) {
635 throw new Error("No fue posible obtener token anónimo. HTTP " + res.status);
636 }
637
638 var data = await res.json();
639
640 if (!data || !data.access_token) {
641 throw new Error("La respuesta de token no contiene access_token");
642 }
643
644 setStoredToken(data.access_token);
645 return data.access_token;
646 }
647
648 async function ensureAnonymousToken() {
649 var token = getStoredToken();
650 if (token && token.trim().length > 0) return token;
651 return await requestAnonymousToken();
652 }
653
654 function escapeHtml(text) {
655 return String(text || "")
656 .replace(/&/g, "&")
657 .replace(/</g, "<")
658 .replace(/>/g, ">")
659 .replace(/"/g, """)
660 .replace(/'/g, "'");
661 }
662
663 function sanitizeText(text) {
664 var t = String(text || "");
665 t = t.replace(/^\uFEFF/, "");
666 t = t.replace(/^[\s\r\n]+/, "");
667 t = t.replace(/[\s\r\n]+$/, "");
668 return t;
669 }
670
671 function scrollToBottom() {
672 var el = document.getElementById("mhMessages");
673 if (!el) return;
674 el.scrollTop = el.scrollHeight;
675 }
676
677 function addWelcomeCard() {
678 if (document.getElementById("mhWelcomeCard")) return;
679
680 var container = document.getElementById("mhMessages");
681 var wrapper = document.createElement("div");
682 wrapper.className = "mh-msg";
683 wrapper.innerHTML = `
684 <div class="mh-avatar">
685 <img src="${BOT_AVATAR}" alt="bot"/>
686 </div>
687 <div>
688 <div id="mhWelcomeCard" class="mh-welcome-card">
689 <h3 class="mh-welcome-title">Asistente virtual</h3>
690 <p class="mh-welcome-text">${escapeHtml(WELCOME_TEXT)}</p>
691 <button id="mhStartBtn" class="mh-welcome-btn">Iniciar conversación</button>
692 </div>
693 <div class="mh-meta-time">${nowLabel()}</div>
694 </div>
695 `;
696 container.appendChild(wrapper);
697
698 setTimeout(function () {
699 var btn = document.getElementById("mhStartBtn");
700 if (btn) {
701 btn.addEventListener("click", function () {
702 var input = document.getElementById("mhInput");
703 if (input) input.focus();
704 });
705 }
706 }, 0);
707
708 scrollToBottom();
709 }
710
711 function addMessage(role, text) {
712 var container = document.getElementById("mhMessages");
713 var isUser = role === "user";
714
715 var msg = document.createElement("div");
716 msg.className = "mh-msg" + (isUser ? " mh-msg--user" : "");
717 var avatarUrl = isUser ? USER_AVATAR : BOT_AVATAR;
718
719 var bubbleClass =
720 "mh-bubble " + (isUser ? "mh-bubble--user" : "mh-bubble--bot");
721 var safeText = escapeHtml(sanitizeText(text));
722
723 if (isUser) {
724 msg.innerHTML = `<div class="${bubbleClass}"><span class="mh-bubble__text">${safeText}</span></div>`;
725 } else {
726 msg.innerHTML = `
727 <div class="mh-avatar">
728 <img src="${avatarUrl}" alt="bot"/>
729 </div>
730 <div class="${bubbleClass}">
731 <span class="mh-bubble__text">${safeText}</span>
732 </div>
733 `;
734 }
735
736 container.appendChild(msg);
737 scrollToBottom();
738 }
739
740 function addLoadingMessage(customText) {
741 var container = document.getElementById("mhMessages");
742 if (!container) return null;
743
744 var id = "mh-loading-" + Date.now();
745 var msg = document.createElement("div");
746 msg.className = "mh-msg";
747 msg.id = id;
748
749 var safeText = escapeHtml(
750 sanitizeText(
751 customText ||
752 "Estamos procesando tu pregunta, espera un momento, por favor."
753 )
754 );
755
756 msg.innerHTML = `
757 <div class="mh-avatar">
758 <img src="${BOT_AVATAR}" alt="bot"/>
759 </div>
760 <div class="mh-bubble mh-bubble--bot mh-bubble--loading">
761 <span class="mh-bubble__text">${safeText}</span>
762 </div>
763 `;
764
765 container.appendChild(msg);
766 scrollToBottom();
767 return id;
768 }
769
770 function removeMessageById(id) {
771 if (!id) return;
772 var el = document.getElementById(id);
773 if (el && el.parentNode) el.parentNode.removeChild(el);
774 }
775
776 async function fetchWithAuth(url, options) {
777 await ensureAnonymousToken();
778
779 var baseHeaders = Object.assign(
780 { "Content-Type": "application/json" },
781 getAuthHeader()
782 );
783
784 var finalOptions = Object.assign({}, options || {});
785 finalOptions.headers = Object.assign(baseHeaders, (options && options.headers) || {});
786
787 var res = await fetch(url, finalOptions);
788
789 if (res.status === 401) {
790 clearStoredToken();
791 await ensureAnonymousToken();
792
793 finalOptions.headers = Object.assign(
794 { "Content-Type": "application/json" },
795 getAuthHeader(),
796 (options && options.headers) || {}
797 );
798
799 res = await fetch(url, finalOptions);
800 }
801
802 return res;
803 }
804
805 async function loadHistory(sessionId) {
806 try {
807 var res = await fetchWithAuth(
808 API_BASE + "/api/chat/sessions/" + encodeURIComponent(sessionId),
809 {
810 method: "GET",
811 }
812 );
813
814 if (!res.ok) return;
815
816 var data = await res.json();
817 if (!data || !data.messages || !Array.isArray(data.messages)) return;
818
819 data.messages.forEach(function (m) {
820 if (!m || !m.role) return;
821 if (m.role === "user" && m.content) addMessage("user", m.content);
822 if (m.role === "assistant" && m.content)
823 addMessage("assistant", m.content);
824 });
825 } catch (e) {
826 console.warn("No se pudo cargar historial:", e);
827 }
828 }
829
830 async function sendToBackend(sessionId, sessionName, queryText) {
831 var payload = {
832 session_id: sessionId,
833 session_name: sessionName || "Chat",
834 query: queryText,
835 flag_modifier: false,
836 model_name: "gpt-5-mini",
837 search_tool: true,
838 };
839
840 var res = await fetchWithAuth(API_BASE + "/api/chat/message", {
841 method: "POST",
842 body: JSON.stringify(payload),
843 });
844
845 if (!res.ok) {
846 var msg = "Error HTTP " + res.status;
847 try {
848 var err = await res.json();
849 if (err && err.detail) msg = err.detail;
850 } catch (_) {}
851 throw new Error(msg);
852 }
853
854 return await res.json();
855 }
856
857 function setSending(isSending) {
858 var btn = document.getElementById("mhSendBtn");
859 var input = document.getElementById("mhInput");
860 var mic = document.getElementById("mhMicBtn");
861
862 if (btn) btn.disabled = !!isSending;
863 if (input) input.disabled = !!isSending;
864 if (mic) mic.disabled = !!isSending;
865 }
866
867 async function handleSend() {
868 var input = document.getElementById("mhInput");
869 if (!input) return;
870
871 var text = (input.value || "").trim();
872 if (!text) return;
873
874 input.value = "";
875 addMessage("user", text);
876 setSending(true);
877
878 var sessionId = getOrCreateSessionId();
879 var sessionName = "Chat SIIF";
880
881 var loadingId = addLoadingMessage(
882 "Estamos procesando tu pregunta, espera un momento, por favor."
883 );
884
885 try {
886 var data = await sendToBackend(sessionId, sessionName, text);
887 console.log("Respuesta del backend:", data);
888 removeMessageById(loadingId);
889 addMessage(
890 "assistant",
891 data.text ||
892 data.answer ||
893 data.response ||
894 data.message ||
895 JSON.stringify(data) ||
896 "(sin respuesta)"
897 );
898 } catch (e) {
899 console.error("Error al procesar solicitud:", e);
900 removeMessageById(loadingId);
901 addMessage(
902 "assistant",
903 "Se presentó un inconveniente al procesar la solicitud."
904 );
905 } finally {
906 setSending(false);
907 input.focus();
908 }
909 }
910
911 var SpeechRecognition =
912 window.SpeechRecognition || window.webkitSpeechRecognition;
913 var recognizer = null;
914 var isRecording = false;
915
916 function setMicUI(recording) {
917 var mic = document.getElementById("mhMicBtn");
918 var input = document.getElementById("mhInput");
919 if (!mic || !input) return;
920
921 if (recording) {
922 mic.classList.add("is-recording");
923 input.placeholder = "Escuchando...";
924 } else {
925 mic.classList.remove("is-recording");
926 input.placeholder = "Escriba su mensaje";
927 }
928 }
929
930 function initRecognizer() {
931 if (!SpeechRecognition) return null;
932
933 var r = new SpeechRecognition();
934 r.lang = "es-CO";
935 r.interimResults = true;
936 r.continuous = true;
937
938 r.onresult = function (event) {
939 var input = document.getElementById("mhInput");
940 if (!input) return;
941
942 var finalText = "";
943 var interimText = "";
944
945 for (var i = event.resultIndex; i < event.results.length; i++) {
946 var res = event.results[i];
947 var txt =
948 res[0] && res[0].transcript ? res[0].transcript : "";
949 if (res.isFinal) finalText += txt;
950 else interimText += txt;
951 }
952
953 var current = input.getAttribute("data-final") || "";
954 if (finalText) {
955 current = (current + " " + finalText)
956 .replace(/\s+/g, " ")
957 .trim();
958 input.setAttribute("data-final", current);
959 }
960
961 var combined = (current + " " + interimText)
962 .replace(/\s+/g, " ")
963 .trim();
964 input.value = combined;
965 };
966
967 r.onerror = function () {
968 stopDictation();
969 addMessage(
970 "assistant",
971 "No fue posible iniciar el dictado. Verifique permisos del micrófono."
972 );
973 };
974
975 r.onend = function () {
976 if (isRecording) {
977 isRecording = false;
978 setMicUI(false);
979 }
980 };
981
982 return r;
983 }
984
985 async function startDictation() {
986 if (!SpeechRecognition) {
987 addMessage(
988 "assistant",
989 "El dictado no está soportado en este navegador. Use Chrome o Edge."
990 );
991 return;
992 }
993 if (!recognizer) recognizer = initRecognizer();
994
995 var input = document.getElementById("mhInput");
996 if (input)
997 input.setAttribute("data-final", (input.value || "").trim());
998
999 recognizer.start();
1000 isRecording = true;
1001 setMicUI(true);
1002 }
1003
1004 function stopDictation() {
1005 if (recognizer && isRecording) {
1006 try {
1007 recognizer.stop();
1008 } catch (_) {}
1009 }
1010 isRecording = false;
1011 setMicUI(false);
1012 }
1013
1014 async function handleMicClick() {
1015 var sendBtn = document.getElementById("mhSendBtn");
1016 if (sendBtn && sendBtn.disabled) return;
1017
1018 try {
1019 if (!isRecording) await startDictation();
1020 else stopDictation();
1021 } catch (e) {
1022 console.error(e);
1023 stopDictation();
1024 addMessage(
1025 "assistant",
1026 "No fue posible acceder al micrófono. Verifique los permisos del navegador."
1027 );
1028 }
1029 }
1030
1031 function mountUI() {
1032 var webchat = document.getElementById("webchat");
1033 if (!webchat) return;
1034
1035 webchat.innerHTML = `
1036 <div id="mhMessages" class="mh-chat__messages"></div>
1037 <div class="mh-chat__composer">
1038 <div class="mh-inputwrap">
1039 <input id="mhInput" class="mh-input" type="text" placeholder="Escriba su mensaje" />
1040 <button id="mhMicBtn" class="mh-mic-in" type="button" aria-label="Dictar" title="Dictar">
1041 <svg width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
1042 <path fill="#6b6b6b" d="M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.92V21h2v-3.08A7 7 0 0 0 19 11h-2z"/>
1043 </svg>
1044 </button>
1045 </div>
1046 <button id="mhSendBtn" class="mh-send" type="button">Enviar</button>
1047 </div>
1048 `;
1049
1050 addWelcomeCard();
1051
1052 var btn = document.getElementById("mhSendBtn");
1053 var input = document.getElementById("mhInput");
1054 var mic = document.getElementById("mhMicBtn");
1055
1056 if (btn) btn.addEventListener("click", handleSend);
1057 if (mic) mic.addEventListener("click", handleMicClick);
1058
1059 if (input) {
1060 input.addEventListener("keydown", function (e) {
1061 if (e.key === "Enter") handleSend();
1062 });
1063 }
1064 }
1065
1066 return {
1067 init: async function () {
1068 await ensureAnonymousToken();
1069 mountUI();
1070 var sessionId = getOrCreateSessionId();
1071 await loadHistory(sessionId);
1072 addWelcomeCard();
1073 },
1074 };
1075 })();
1076
1077 $(document).ready(function () {
1078 botServices.init();
1079 });
1080 </script>
1081 </head>
1082
1083 <body id="master_chat">
1084 <div id="webchat" role="main"></div>
1085 </body>
1086
1087 </div>
1088</div>
1089<script>
1090 // Guard para evitar doble inicialización en portales/portlets
1091 if (!window.__MH_WRAPPER_INITED__) {
1092 window.__MH_WRAPPER_INITED__ = true;
1093
1094 var index = (function () {
1095 var btn = document.getElementById("bext-chat-bot__btn_collapse");
1096 var avatarImg = document.getElementById("bext-chat-bot__btn_img");
1097 var arrowIcon = document.getElementById("bext-chat-bot__arrows_i");
1098 var iframeEl = document.getElementById("bext-chat-bot__iframe");
1099
1100 function postToIframe(payload) {
1101 if (!iframeEl || !iframeEl.contentWindow) return;
1102 iframeEl.contentWindow.postMessage(payload, "*");
1103 }
1104
1105 function openChatUI() {
1106 var chat = document.getElementById("bext-chat-bot__iframe");
1107 var main_div = document.getElementById("bot__wrapper__div");
1108 var header2 = document.getElementById("bext-chat-bot__btn");
1109 var header = document.getElementById("header_chat");
1110 var btn_arrw_header = document.getElementById("bext-chat-bot__arrw");
1111
1112 main_div.classList.add("bext-chat-bot__wrapper");
1113 header2.classList.add("bext-chat-bot__header");
1114 btn_arrw_header.classList.add("bext-chat-bot__arrows_container");
1115
1116 avatarImg.classList.remove("closelogo-close");
1117 avatarImg.classList.add("closelogo");
1118 avatarImg.src = "https://stonlineprodeastusbots2.blob.core.windows.net/imagenes/jarvisCut.png";
1119
1120 chat.classList.remove("bext-chat-bot__close");
1121
1122 main_div.classList.remove("bext-chat-bot__wrapper2");
1123 header2.classList.remove("bext-chat-bot__header2");
1124 arrowIcon.classList.remove("gg-chevron-up");
1125 arrowIcon.classList.add("gg-chevron-down");
1126
1127 header.style.display = "block";
1128
1129 postToIframe({ type: "CHAT_STATE", open: true });
1130 }
1131
1132 function closeChatUI() {
1133 var chat = document.getElementById("bext-chat-bot__iframe");
1134 var main_div = document.getElementById("bot__wrapper__div");
1135 var header2 = document.getElementById("bext-chat-bot__btn");
1136 var header = document.getElementById("header_chat");
1137 var btn_arrw_header = document.getElementById("bext-chat-bot__arrw");
1138
1139 chat.classList.add("bext-chat-bot__close");
1140 header.classList.add("bext-chat-bot__close");
1141 btn_arrw_header.classList.add("bext-chat-bot__close");
1142
1143 avatarImg.classList.add("closelogo-close");
1144 avatarImg.classList.remove("closelogo");
1145 avatarImg.src = "https://stonlineprodeastusbots2.blob.core.windows.net/imagenes/jarvis.png";
1146
1147 main_div.classList.add("bext-chat-bot__wrapper2");
1148 header2.classList.add("bext-chat-bot__header2");
1149
1150 main_div.classList.remove("bext-chat-bot__wrapper");
1151 header2.classList.remove("bext-chat-bot__header");
1152 btn_arrw_header.classList.remove("bext-chat-bot__arrows_container");
1153
1154 arrowIcon.classList.remove("gg-chevron-down");
1155 arrowIcon.classList.add("gg-chevron-up");
1156
1157 header.style.display = "none";
1158
1159 postToIframe({ type: "CHAT_STATE", open: false });
1160 }
1161
1162 return {
1163 init: function () {
1164 if (btn) btn.addEventListener("click", function () { index.toggle(); });
1165 if (avatarImg) avatarImg.addEventListener("click", function () { index.toggle(); });
1166
1167 // Por defecto: abrir al cargar (como lo tenías con btn.click())
1168 openChatUI();
1169 },
1170
1171 toggle: function () {
1172 var chat = document.getElementById("bext-chat-bot__iframe");
1173 var isClosed = chat && chat.classList.contains("bext-chat-bot__close");
1174
1175 if (isClosed) openChatUI();
1176 else closeChatUI();
1177 }
1178 };
1179 })();
1180
1181 $(document).ready(function () {
1182 index.init();
1183 });
1184 }
1185</script>












