botql 1.1.2 → 1.2.2

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/botql.browser.js CHANGED
@@ -22,23 +22,29 @@ var BotQL = (() => {
22
22
  var TokenType = { KEYWORD: "KEYWORD", STRING: "STRING", NUMBER: "NUMBER", IDENT: "IDENT", SYMBOL: "SYMBOL", EOF: "EOF" };
23
23
  var KEYWORDS = /* @__PURE__ */ new Set(["CREATE", "BOT", "PLATFORM", "CONNECT", "TABLE", "PREVENT", "DEFAULT", "ON", "START", "MESSAGE", "FROM", "WHEN", "CONTAINS", "OR", "OTHERWISE", "KEYWORDS", "THINK", "WAITING", "REPLY", "TO", "FORWARD", "PARSE", "SEND", "INSERT", "INTO", "VALUES", "UPDATE", "SET", "WHERE", "RUN", "IMPORT", "AS", "RESPONSE"]);
24
24
  var Token = class {
25
- constructor(e, t, s) {
26
- this.type = e, this.value = t, this.line = s;
25
+ constructor(e, t, s, r, i) {
26
+ this.type = e, this.value = t, this.line = s, this.start = r, this.end = i;
27
+ }
28
+ };
29
+ var BotQLSyntaxError = class extends Error {
30
+ constructor(e, { line: t = null, start: s = 0, end: r = null, near: i = null } = {}) {
31
+ super(e), this.name = "BotQLSyntaxError", this.line = t, this.start = s, this.end = null === r ? s + 1 : r, this.near = i;
27
32
  }
28
33
  };
29
34
  var Tokenizer = class {
30
35
  constructor(e) {
31
36
  this.source = e, this.pos = 0, this.line = 1, this.tokens = [];
32
37
  }
33
- error(e) {
34
- throw Error(`syntax error: ${e}`);
38
+ error(e, t, s) {
39
+ let r = void 0 !== t ? t : this.pos;
40
+ throw new BotQLSyntaxError(`syntax error: ${e}`, { line: this.line, start: r, end: s });
35
41
  }
36
42
  peekChar(e = 0) {
37
43
  return this.source[this.pos + e];
38
44
  }
39
45
  tokenize() {
40
46
  for (; this.pos < this.source.length; ) {
41
- let e = this.peekChar();
47
+ let e = this.peekChar(), t = this.pos;
42
48
  if ("\n" === e) {
43
49
  this.line++, this.pos++;
44
50
  continue;
@@ -51,39 +57,24 @@ var BotQL = (() => {
51
57
  for (; this.pos < this.source.length && "\n" !== this.peekChar(); ) this.pos++;
52
58
  continue;
53
59
  }
54
- if ('"' === e || "'" === e) {
55
- this.tokens.push(this.readString(e));
56
- continue;
57
- }
58
- if (/[0-9]/.test(e)) {
59
- this.tokens.push(this.readNumber());
60
- continue;
61
- }
62
- if ("{}(),.=+*;".includes(e)) {
63
- this.tokens.push(new Token(TokenType.SYMBOL, e, this.line)), this.pos++;
64
- continue;
65
- }
66
- if (/[A-Za-z_]/.test(e)) {
67
- let t = this.readWord();
68
- this.tokens.push(t);
69
- continue;
70
- }
71
- this.error(`unexpected character: "${e}"`);
60
+ let s;
61
+ '"' === e || "'" === e ? s = this.readString(e) : /[0-9]/.test(e) ? s = this.readNumber() : "{}(),.=+*;".includes(e) ? (s = new Token(TokenType.SYMBOL, e, this.line), this.pos++) : /[A-Za-z_]/.test(e) ? s = this.readWord() : this.error(`unexpected character: "${e}"`, t, t + 1), s.start = t, s.end = this.pos, this.tokens.push(s);
72
62
  }
73
- return this.tokens.push(new Token(TokenType.EOF, null, this.line)), this.tokens;
63
+ let r = this.source.length, i = new Token(TokenType.EOF, null, this.line, r, r);
64
+ return this.tokens.push(i), this.tokens;
74
65
  }
75
66
  readString(e) {
76
- let t = this.line;
67
+ let t = this.line, s = this.pos;
77
68
  this.pos++;
78
- let s = "";
69
+ let r = "";
79
70
  for (; this.pos < this.source.length && this.peekChar() !== e; ) {
80
71
  if ("\\" === this.peekChar()) {
81
- this.pos++, s += this.peekChar(), this.pos++;
72
+ this.pos++, r += this.peekChar(), this.pos++;
82
73
  continue;
83
74
  }
84
- "\n" === this.peekChar() && this.line++, s += this.peekChar(), this.pos++;
75
+ "\n" === this.peekChar() && this.line++, r += this.peekChar(), this.pos++;
85
76
  }
86
- return this.peekChar() !== e && this.error("unterminated string"), this.pos++, new Token(TokenType.STRING, s, t);
77
+ return this.peekChar() !== e && this.error("unterminated string", s, this.pos), this.pos++, new Token(TokenType.STRING, r, t);
87
78
  }
88
79
  readNumber() {
89
80
  let e = this.line, t = "";
@@ -99,11 +90,11 @@ var BotQL = (() => {
99
90
  };
100
91
  var Parser = class {
101
92
  constructor(e) {
102
- this.tokens = new Tokenizer(e).tokenize(), this.pos = 0;
93
+ this.source = e, this.tokens = new Tokenizer(e).tokenize(), this.pos = 0;
103
94
  }
104
95
  error(e) {
105
- let t = this.current();
106
- throw Error(`syntax error: ${e}, near "${t ? t.value : "EOF"}"`);
96
+ let t = this.current(), s = t && t.type !== TokenType.EOF ? t.value : "EOF", r = t ? t.start : this.source.length, i = t ? t.end : this.source.length;
97
+ throw new BotQLSyntaxError(`syntax error: ${e}, near "${s}"`, { line: t ? t.line : null, start: r, end: i, near: s });
107
98
  }
108
99
  current() {
109
100
  return this.tokens[this.pos];
@@ -147,7 +138,7 @@ var BotQL = (() => {
147
138
  return [this.parseStatement()];
148
139
  }
149
140
  parseStatement() {
150
- return this.atKeyword("CREATE") ? this.parseCreate() : this.atKeyword("PLATFORM") ? this.parsePlatform() : this.atKeyword("CONNECT") ? this.parseConnect() : this.atKeyword("ON") ? this.parseOn() : this.atKeyword("WHEN") ? this.parseWhen() : this.atKeyword("OTHERWISE") ? this.parseOtherwise() : this.atKeyword("REPLY") ? this.parseReply() : this.atKeyword("THINK") ? this.parseThink() : this.atKeyword("FORWARD") ? this.parseForward() : this.atKeyword("PARSE") ? this.parseParseSignal() : this.atKeyword("SEND") ? this.parseSend() : this.atKeyword("INSERT") ? this.parseInsert() : this.atKeyword("UPDATE") ? this.parseUpdate() : this.atKeyword("RUN") ? this.parseRunBot() : this.atKeyword("IMPORT") ? this.parseImport() : void this.error("unexpected statement");
141
+ return this.atKeyword("CREATE") ? this.parseCreate() : this.atKeyword("PLATFORM") ? this.parsePlatform() : this.atKeyword("CONNECT") ? this.parseConnect() : this.atKeyword("ON") ? this.parseOn() : this.atKeyword("WHEN") ? this.parseWhen() : this.atKeyword("OTHERWISE") ? this.parseOtherwise() : this.atKeyword("REPLY") ? this.parseReply() : this.atKeyword("THINK") ? this.parseThink() : this.atKeyword("WAITING") ? this.parseWaitingStatement() : this.atKeyword("FORWARD") ? this.parseForward() : this.atKeyword("PARSE") ? this.parseParseSignal() : this.atKeyword("SEND") ? this.parseSend() : this.atKeyword("INSERT") ? this.parseInsert() : this.atKeyword("UPDATE") ? this.parseUpdate() : this.atKeyword("RUN") ? this.parseRunBot() : this.atKeyword("IMPORT") ? this.parseImport() : void this.error("unexpected statement");
151
142
  }
152
143
  parseCreate() {
153
144
  if (this.expectKeyword("CREATE"), this.atKeyword("BOT")) {
@@ -270,8 +261,12 @@ var BotQL = (() => {
270
261
  tryParseWaiting() {
271
262
  if (!this.atKeyword("WAITING")) return null;
272
263
  this.advance(), this.expect(TokenType.SYMBOL, "(");
273
- let e = null, t = null;
274
- return !this.at(TokenType.SYMBOL, ")") && (e = this.parseFileName(), this.at(TokenType.SYMBOL, ",") && (this.advance(), t = this.parseExpression())), this.expect(TokenType.SYMBOL, ")"), { file: e, seconds: t };
264
+ let e = null, t = null, s = null;
265
+ return !this.at(TokenType.SYMBOL, ")") && (this.at(TokenType.STRING) ? t = this.advance().value : e = this.parseFileName(), this.at(TokenType.SYMBOL, ",") && (this.advance(), s = this.parseExpression())), this.expect(TokenType.SYMBOL, ")"), { file: e, text: t, seconds: s };
266
+ }
267
+ parseWaitingStatement() {
268
+ let e = this.tryParseWaiting();
269
+ return { type: "Waiting", file: e.file, text: e.text, seconds: e.seconds };
275
270
  }
276
271
  parseReply() {
277
272
  this.expectKeyword("REPLY");
@@ -374,7 +369,7 @@ var BotQL = (() => {
374
369
  this.error("invalid expression");
375
370
  }
376
371
  };
377
- module.exports = { Parser, Tokenizer, TokenType, KEYWORDS };
372
+ module.exports = { Parser, Tokenizer, TokenType, KEYWORDS, BotQLSyntaxError };
378
373
  }
379
374
  });
380
375
 
@@ -530,505 +525,221 @@ var BotQL = (() => {
530
525
  var require_RAG = __commonJS({
531
526
  "RAG.js"(exports, module) {
532
527
  "use strict";
533
- var STOPWORDS = /* @__PURE__ */ new Set([
534
- "a",
535
- "o",
536
- "as",
537
- "os",
538
- "de",
539
- "da",
540
- "do",
541
- "das",
542
- "dos",
543
- "e",
544
- "ou",
545
- "que",
546
- "um",
547
- "uma",
548
- "uns",
549
- "umas",
550
- "em",
551
- "no",
552
- "na",
553
- "nos",
554
- "nas",
555
- "por",
556
- "para",
557
- "com",
558
- "sem",
559
- "se",
560
- "foi",
561
- "ser",
562
- "sao",
563
- "esta",
564
- "estao",
565
- "ao",
566
- "aos",
567
- "mas",
568
- "como",
569
- "tem",
570
- "ter",
571
- "nao",
572
- "sim",
573
- "meu",
574
- "minha",
575
- "seu",
576
- "sua",
577
- "eu",
578
- "tu",
579
- "ele",
580
- "ela",
581
- "nos",
582
- "vos",
583
- "eles",
584
- "elas",
585
- "isso",
586
- "isto",
587
- "aquilo",
588
- "quando",
589
- "onde",
590
- "porque",
591
- "qual",
592
- "quais",
593
- "muito",
594
- "muita",
595
- "ja",
596
- "so"
597
- ]);
598
- function normalizar(texto) {
599
- return texto.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
528
+ var STOPWORDS = /* @__PURE__ */ new Set(["a", "o", "as", "os", "de", "da", "do", "das", "dos", "e", "ou", "que", "um", "uma", "uns", "umas", "em", "no", "na", "nos", "nas", "por", "para", "com", "sem", "se", "foi", "ser", "sao", "esta", "estao", "ao", "aos", "mas", "como", "tem", "ter", "nao", "sim", "meu", "minha", "seu", "sua", "eu", "tu", "ele", "ela", "nos", "vos", "eles", "elas", "isso", "isto", "aquilo", "quando", "onde", "porque", "qual", "quais", "muito", "muita", "ja", "so"]);
529
+ function normalizar(e) {
530
+ return e.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
600
531
  }
601
532
  var SUFIXOS_ADJETIVO_ADVERBIO = ["issimamente", "issimo", "issima", "mente"];
602
533
  var SUFIXOS_NOMINALIZACAO = ["acoes", "acao", "imentos", "imento", "idades", "idade"];
603
- function stem(palavra) {
604
- let p = palavra;
605
- for (const suf of SUFIXOS_ADJETIVO_ADVERBIO) {
606
- if (p.length > suf.length + 3 && p.endsWith(suf)) {
607
- p = p.slice(0, -suf.length);
608
- break;
609
- }
534
+ function stem(e) {
535
+ let t = e;
536
+ for (let i of SUFIXOS_ADJETIVO_ADVERBIO) if (t.length > i.length + 3 && t.endsWith(i)) {
537
+ t = t.slice(0, -i.length);
538
+ break;
610
539
  }
611
- for (const suf of SUFIXOS_NOMINALIZACAO) {
612
- if (p.length > suf.length + 3 && p.endsWith(suf)) {
613
- p = p.slice(0, -suf.length);
614
- break;
615
- }
540
+ for (let o of SUFIXOS_NOMINALIZACAO) if (t.length > o.length + 3 && t.endsWith(o)) {
541
+ t = t.slice(0, -o.length);
542
+ break;
616
543
  }
617
- if (p.length > 4 && p.endsWith("s") && !p.endsWith("ns")) {
618
- p = p.slice(0, -1);
619
- }
620
- return p;
544
+ return t.length > 4 && t.endsWith("s") && !t.endsWith("ns") && (t = t.slice(0, -1)), t;
621
545
  }
622
- function tokenizar(texto) {
623
- return normalizar(texto).toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((palavra) => palavra.length > 1 && !STOPWORDS.has(palavra)).map(stem);
546
+ function tokenizar(e) {
547
+ return normalizar(e).toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((e2) => e2.length > 1 && !STOPWORDS.has(e2)).map(stem);
624
548
  }
625
- function distanciaEdicao(a, b) {
626
- if (a === b) return 0;
627
- const la = a.length;
628
- const lb = b.length;
629
- if (la === 0) return lb;
630
- if (lb === 0) return la;
631
- let linhaAnterior = new Array(lb + 1);
632
- for (let j = 0; j <= lb; j++) linhaAnterior[j] = j;
633
- for (let i = 1; i <= la; i++) {
634
- const linhaAtual = [i];
635
- for (let j = 1; j <= lb; j++) {
636
- const custo = a[i - 1] === b[j - 1] ? 0 : 1;
637
- linhaAtual[j] = Math.min(
638
- linhaAtual[j - 1] + 1,
639
- linhaAnterior[j] + 1,
640
- linhaAnterior[j - 1] + custo
641
- );
549
+ function distanciaEdicao(e, t) {
550
+ if (e === t) return 0;
551
+ let i = e.length, o = t.length;
552
+ if (0 === i) return o;
553
+ if (0 === o) return i;
554
+ let s = Array(o + 1);
555
+ for (let r = 0; r <= o; r++) s[r] = r;
556
+ for (let n = 1; n <= i; n++) {
557
+ let a = [n];
558
+ for (let l = 1; l <= o; l++) {
559
+ let c = e[n - 1] === t[l - 1] ? 0 : 1;
560
+ a[l] = Math.min(a[l - 1] + 1, s[l] + 1, s[l - 1] + c);
642
561
  }
643
- linhaAnterior = linhaAtual;
562
+ s = a;
644
563
  }
645
- return linhaAnterior[lb];
564
+ return s[o];
646
565
  }
647
- function distanciaMaximaTolerada(tamanho) {
648
- if (tamanho <= 4) return 0;
649
- if (tamanho <= 7) return 1;
650
- return 2;
566
+ function distanciaMaximaTolerada(e) {
567
+ return e <= 4 ? 0 : e <= 7 ? 1 : 2;
651
568
  }
652
- var BM25_K1 = 1.5;
653
- var BM25_B = 0.75;
654
- var PESO_BM25 = 1;
655
- var PESO_FRASE = 2.5;
656
- var PESO_FUZZY = 0.4;
657
- var CONFIANCA_ALTA = 0.55;
658
- var CONFIANCA_MINIMA = 0.15;
659
- var MARGEM_MINIMA = 0.12;
660
- var SIMILARIDADE_DUPLICADA = 0.75;
661
- function extrairNomesProprios(texto) {
662
- const palavras = texto.split(/\s+/);
663
- const encontrados = [];
664
- for (let i = 1; i < palavras.length; i++) {
665
- const limpa = palavras[i].replace(/^[(["']+|[.,;:!?)\]"']+$/g, "");
666
- if (/^[A-ZÀ-Ý][a-zà-ÿ]+$/.test(limpa)) {
667
- encontrados.push(limpa);
668
- }
569
+ function extrairNomesProprios(e) {
570
+ let t = e.split(/\s+/), i = [];
571
+ for (let o = 1; o < t.length; o++) {
572
+ let s = t[o].replace(/^[(["']+|[.,;:!?)\]"']+$/g, "");
573
+ /^[A-ZÀ-Ý][a-zà-ÿ]+$/.test(s) && i.push(s);
669
574
  }
670
- return encontrados;
575
+ return i;
671
576
  }
672
577
  var KnowledgeIndex = class _KnowledgeIndex {
673
- /**
674
- * @param {string} sourceText Conteúdo do ficheiro de conhecimento.
675
- * @param {Object} [options]
676
- * @param {Record<string,string>} [options.synonyms] Mapa de termo -> termo
677
- * canónico, para ligar manualmente palavras que o stemmer não junta
678
- * sozinho (ex: { horas: 'horario' }).
679
- */
680
- constructor(sourceText, options = {}) {
681
- this.synonyms = {};
682
- for (const [de, para] of Object.entries(options.synonyms || {})) {
683
- this.synonyms[stem(normalizar(de.toLowerCase()))] = stem(normalizar(para.toLowerCase()));
684
- }
685
- this.blocks = _KnowledgeIndex.parseBlocks(sourceText);
686
- this._buildIndex();
578
+ constructor(e, t = {}) {
579
+ for (let [i, o] of (this.synonyms = {}, Object.entries(t.synonyms || {}))) this.synonyms[stem(normalizar(i.toLowerCase()))] = stem(normalizar(o.toLowerCase()));
580
+ this.blocks = _KnowledgeIndex.parseBlocks(e), this._buildIndex();
687
581
  }
688
- static parseBlocks(sourceText) {
689
- return sourceText.split(/\n\s*\n/).map((bloco) => bloco.trim()).filter((bloco) => bloco.length > 0);
582
+ static parseBlocks(e) {
583
+ return e.split(/\n\s*\n/).map((e2) => e2.trim()).filter((e2) => e2.length > 0);
690
584
  }
691
- _aplicarSinonimos(tokens) {
692
- return tokens.map((t) => this.synonyms[t] || t);
585
+ _aplicarSinonimos(e) {
586
+ return e.map((e2) => this.synonyms[e2] || e2);
693
587
  }
694
588
  _buildIndex() {
695
- this.docs = this.blocks.map((bloco) => tokenizar(bloco));
696
- this.docTextNormalizado = this.blocks.map((bloco) => normalizar(bloco).toLowerCase());
697
- this.docFreq = /* @__PURE__ */ new Map();
698
- this.vocabulario = /* @__PURE__ */ new Set();
699
- for (const tokens of this.docs) {
700
- const vistas = new Set(tokens);
701
- for (const palavra of vistas) {
702
- this.docFreq.set(palavra, (this.docFreq.get(palavra) || 0) + 1);
703
- this.vocabulario.add(palavra);
704
- }
589
+ for (let e of (this.docs = this.blocks.map((e2) => tokenizar(e2)), this.docTextNormalizado = this.blocks.map((e2) => normalizar(e2).toLowerCase()), this.docPalavrasSignificativas = this.blocks.map((e2) => this._palavrasSignificativas(e2)), this.docFreq = /* @__PURE__ */ new Map(), this.vocabulario = /* @__PURE__ */ new Set(), this.docs)) {
590
+ let t = new Set(e);
591
+ for (let i of t) this.docFreq.set(i, (this.docFreq.get(i) || 0) + 1), this.vocabulario.add(i);
705
592
  }
706
- this.listaVocabulario = Array.from(this.vocabulario);
707
- this.totalDocs = this.docs.length;
708
- this.avgDocLen = this.totalDocs === 0 ? 0 : this.docs.reduce((soma, tokens) => soma + tokens.length, 0) / this.totalDocs;
709
- }
710
- _idf(palavra) {
711
- const n = this.docFreq.get(palavra) || 0;
712
- if (n === 0) return 0;
713
- return Math.log(1 + (this.totalDocs - n + 0.5) / (n + 0.5));
714
- }
715
- _termoMaisProximo(termo) {
716
- const tolerancia = distanciaMaximaTolerada(termo.length);
717
- if (tolerancia === 0) return null;
718
- let melhor = null;
719
- let melhorDist = tolerancia + 1;
720
- for (const candidato of this.listaVocabulario) {
721
- if (Math.abs(candidato.length - termo.length) > tolerancia) continue;
722
- const d = distanciaEdicao(termo, candidato);
723
- if (d < melhorDist) {
724
- melhorDist = d;
725
- melhor = candidato;
726
- }
593
+ this.listaVocabulario = Array.from(this.vocabulario), this.totalDocs = this.docs.length, this.avgDocLen = 0 === this.totalDocs ? 0 : this.docs.reduce((e, t) => e + t.length, 0) / this.totalDocs;
594
+ }
595
+ _idf(e) {
596
+ let t = this.docFreq.get(e) || 0;
597
+ return 0 === t ? 0 : Math.log(1 + (this.totalDocs - t + 0.5) / (t + 0.5));
598
+ }
599
+ _termoMaisProximo(e) {
600
+ let t = distanciaMaximaTolerada(e.length);
601
+ if (0 === t) return null;
602
+ let i = null, o = t + 1;
603
+ for (let s of this.listaVocabulario) {
604
+ if (Math.abs(s.length - e.length) > t) continue;
605
+ let r = distanciaEdicao(e, s);
606
+ r < o && (o = r, i = s);
727
607
  }
728
- return melhorDist <= tolerancia ? melhor : null;
729
- }
730
- _scoreBM25(queryTokens, docIndex) {
731
- const tokens = this.docs[docIndex];
732
- if (tokens.length === 0) return 0;
733
- const termFreq = /* @__PURE__ */ new Map();
734
- for (const t of tokens) termFreq.set(t, (termFreq.get(t) || 0) + 1);
735
- let score = 0;
736
- for (const termo of queryTokens) {
737
- let tf = termFreq.get(termo) || 0;
738
- let idf = this._idf(termo);
739
- let peso = 1;
740
- if (tf === 0) {
741
- const proximo = this._termoMaisProximo(termo);
742
- if (proximo === null) continue;
743
- tf = termFreq.get(proximo) || 0;
744
- if (tf === 0) continue;
745
- idf = this._idf(proximo);
746
- peso = PESO_FUZZY;
608
+ return o <= t ? i : null;
609
+ }
610
+ _scoreBM25(e, t) {
611
+ let i = this.docs[t];
612
+ if (0 === i.length) return 0;
613
+ let o = /* @__PURE__ */ new Map();
614
+ for (let s of i) o.set(s, (o.get(s) || 0) + 1);
615
+ let r = 0;
616
+ for (let n of e) {
617
+ let a = o.get(n) || 0, l = this._idf(n), c = 1;
618
+ if (0 === a) {
619
+ let d = this._termoMaisProximo(n);
620
+ if (null === d || 0 === (a = o.get(d) || 0)) continue;
621
+ l = this._idf(d), c = 0.4;
747
622
  }
748
- const numerador = tf * (BM25_K1 + 1);
749
- const denominador = tf + BM25_K1 * (1 - BM25_B + BM25_B * (tokens.length / this.avgDocLen));
750
- score += peso * idf * (numerador / denominador);
623
+ let h = 2.5 * a, u = a + 1.5 * (0.7 + 0.3 * (i.length / this.avgDocLen));
624
+ r += c * l * (h / u);
751
625
  }
752
- return score;
753
- }
754
- _bonusFrase(message, docIndex) {
755
- const msgNorm = normalizar(message).toLowerCase().replace(/[^a-z0-9\s]/g, " ");
756
- const palavras = msgNorm.split(/\s+/).filter((p) => p.length > 1);
757
- if (palavras.length < 2) return 0;
758
- const textoBloco = this.docTextNormalizado[docIndex];
759
- let bonus = 0;
760
- for (let tamanho = Math.min(6, palavras.length); tamanho >= 2; tamanho--) {
761
- for (let i = 0; i + tamanho <= palavras.length; i++) {
762
- const frase = palavras.slice(i, i + tamanho).join(" ");
763
- if (textoBloco.includes(frase)) {
764
- bonus += tamanho * tamanho;
626
+ return r;
627
+ }
628
+ _palavrasSignificativas(e) {
629
+ return normalizar(e).toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((e2) => e2.length > 1 && !STOPWORDS.has(e2));
630
+ }
631
+ _bonusFrase(e, t) {
632
+ let i = this._palavrasSignificativas(e);
633
+ if (i.length < 2) return 0;
634
+ let o = this.docPalavrasSignificativas[t], s = 0;
635
+ for (let r = Math.min(6, i.length); r >= 2; r--) {
636
+ for (let n = 0; n + r <= i.length; n++) {
637
+ let a = i.slice(n, n + r);
638
+ if (this._contemSequencia(o, a)) {
639
+ s = r;
640
+ break;
765
641
  }
766
642
  }
643
+ if (s > 0) break;
767
644
  }
768
- return bonus;
769
- }
770
- _scoreDoc(message, queryTokens, docIndex) {
771
- const bm25 = this._scoreBM25(queryTokens, docIndex);
772
- const frase = this._bonusFrase(message, docIndex);
773
- return PESO_BM25 * bm25 + PESO_FRASE * frase;
774
- }
775
- // Calcula o score de todos os blocos para a mensagem e devolve ordenado
776
- // do maior para o menor. Base partilhada por search() e analyze().
777
- _rankTodos(message) {
778
- const queryTokens = this._aplicarSinonimos(tokenizar(message));
779
- if (queryTokens.length === 0) return [];
780
- const resultados = [];
781
- for (let i = 0; i < this.totalDocs; i++) {
782
- resultados.push({ index: i, score: this._scoreDoc(message, queryTokens, i) });
783
- }
784
- resultados.sort((a, b) => b.score - a.score);
785
- return resultados;
786
- }
787
- /**
788
- * Procura o bloco mais relevante para a mensagem recebida.
789
- *
790
- * @returns {{text: string, score: number, confidence: number, index: number} | null}
791
- * `confidence` está sempre entre 0 e 1 — não é probabilidade
792
- * estatística real, é uma escala interpretável para decidir um
793
- * limiar no `.sql` (ex: "só responde se confidence > 0.3").
794
- */
795
- search(message) {
796
- if (this.totalDocs === 0) return null;
797
- const ranking = this._rankTodos(message);
798
- if (ranking.length === 0) return null;
799
- const melhor = ranking[0];
800
- if (melhor.score <= 0) return null;
801
- return {
802
- text: this.blocks[melhor.index],
803
- score: melhor.score,
804
- confidence: melhor.score / (melhor.score + 3),
805
- index: melhor.index
806
- };
807
- }
808
- // Jaccard sobre os tokens (já com stem aplicado) de dois blocos já
809
- // indexados. Usado só pra detetar "mesmo conteúdo repetido" (valor
810
- // alto) — dois blocos sobre o mesmo assunto mas com detalhes
811
- // diferentes normalmente NÃO têm jaccard alto (a maior parte da frase
812
- // difere), por isso não serve pra decidir se vale a pena tentar unir.
813
- _similaridadeBlocos(indexA, indexB) {
814
- const a = new Set(this.docs[indexA]);
815
- const b = new Set(this.docs[indexB]);
816
- if (a.size === 0 || b.size === 0) return 0;
817
- let intersecao = 0;
818
- for (const t of a) if (b.has(t)) intersecao++;
819
- const uniao = a.size + b.size - intersecao;
820
- return uniao === 0 ? 0 : intersecao / uniao;
821
- }
822
- // Quantos tokens (com stem) os dois blocos partilham, em termos
823
- // absolutos. Usado como gatilho pra tentar unir: basta partilharem UM
824
- // termo de assunto ("entregamos") — quem garante que a fusão é segura
825
- // não é isto, é a regra de "exatamente um nome próprio diferente em
826
- // cada bloco" dentro de _tentarUnir.
827
- _termosPartilhados(indexA, indexB) {
828
- const a = new Set(this.docs[indexA]);
829
- const b = this.docs[indexB];
830
- let count = 0;
831
- for (const t of b) if (a.has(t)) count++;
832
- return count;
833
- }
834
- // Compara dois blocos concorrentes pelo nome próprio que cada um
835
- // menciona, pra decidir se são "a mesma informação" ou "informação
836
- // complementar que dá pra unir":
837
- // - mesmo nome próprio nos dois (ex: os dois falam de Luanda)
838
- // -> { tipo: 'duplicado' }: é a mesma coisa dita de formas
839
- // diferentes, não há o que unir, usa qualquer um dos dois
840
- // - nomes próprios diferentes (ex: Luanda vs Huambo)
841
- // -> { tipo: 'unido', texto: '...' }: funde numa frase só
842
- // - não dá pra identificar com segurança (nenhum nome próprio, ou
843
- // mais de um em algum dos blocos) -> null: quem chama decide o
844
- // que fazer a seguir (normalmente: reanalisar, ou cair no fallback)
845
- _tentarUnir(textoA, textoB) {
846
- const entidadesA = extrairNomesProprios(textoA);
847
- const entidadesB = extrairNomesProprios(textoB);
848
- if (entidadesA.length !== 1 || entidadesB.length !== 1) return null;
849
- const entidadeA = entidadesA[0];
850
- const entidadeB = entidadesB[0];
851
- if (normalizar(entidadeA).toLowerCase() === normalizar(entidadeB).toLowerCase()) {
852
- return { tipo: "duplicado" };
853
- }
854
- const primeiraPalavra = textoA.trim().split(/\s+/)[0].replace(/[.,;:!?]+$/, "");
855
- return { tipo: "unido", texto: `${primeiraPalavra} em v\xE1rios lugares, como ${entidadeA} e ${entidadeB}.` };
856
- }
857
- // Segunda tentativa de ranking, usada só quando a primeira ficou
858
- // ambígua: descarta metade dos termos da query (os de menor IDF, ou
859
- // seja, os mais genéricos/comuns) e refaz o ranking só com os termos
860
- // mais raros/decisivos. Uma query mais focada às vezes desempata o
861
- // que uma query "cheia" deixa embolado.
862
- _reanalisarFocado(message) {
863
- const tokens = this._aplicarSinonimos(tokenizar(message));
864
- if (tokens.length <= 2) return null;
865
- const comIdf = tokens.map((t) => ({ termo: t, idf: this._idf(t) }));
866
- comIdf.sort((a, b) => b.idf - a.idf);
867
- const focados = comIdf.slice(0, Math.max(1, Math.ceil(tokens.length / 2))).map((x) => x.termo);
868
- const resultados = [];
869
- for (let i = 0; i < this.totalDocs; i++) {
870
- resultados.push({ index: i, score: this._scoreDoc(message, focados, i) });
871
- }
872
- resultados.sort((a, b) => b.score - a.score);
873
- return resultados;
874
- }
875
- /**
876
- * Versão completa da busca: além do melhor bloco, avalia a evidência
877
- * (melhor x segundo colocado) e devolve uma decisão explícita, em vez
878
- * de deixar o `.sql` decidir tudo com um único limiar de confidence.
879
- *
880
- * EvidenceEvaluator: compara o melhor resultado com o segundo. Se os
881
- * dois estão muito próximos, o motor não tem certeza de qual bloco
882
- * responde à pergunta — mesmo que o score absoluto seja alto.
883
- *
884
- * ConfidenceEngine: mesma fórmula de sempre (score / (score + 3)),
885
- * aplicada só ao melhor resultado.
886
- *
887
- * DecisionEngine: cruza confidence com margem para decidir entre:
888
- * - RESPONDER confidence alta e o melhor bloco se destaca do 2o,
889
- * OU os dois concorrentes foram reconciliados (ver
890
- * abaixo) — nesse caso `texto` já vem pronto pra usar
891
- * - REANALISAR os blocos concorrentes são sobre assuntos diferentes
892
- * demais pra reconciliar, e nem a retentativa focada
893
- * resolveu — o `.sql` decide o que fazer (normalmente
894
- * cair no fallback do OR REPLY)
895
- * - UNKNOWN confidence baixa demais, não há bloco que sirva
896
- *
897
- * Reconciliação (só entra quando o resultado não é decisivo de cara):
898
- * 1. Se o melhor e o segundo colocado são basicamente o mesmo
899
- * conteúdo (alta similaridade) — não há nada pra unir, usa o
900
- * melhor tal como está.
901
- * 2. Se são blocos diferentes mas do mesmo assunto, e cada um tem
902
- * exatamente um nome próprio diferente (ex: "entregamos em
903
- * Luanda" / "entregamos no Huambo") — tenta fundir numa frase só
904
- * ("entregamos em vários lugares, como Luanda e Huambo").
905
- * 3. Se nada disso se aplica, tenta de novo com uma versão mais
906
- * enxuta da pergunta (só os termos mais decisivos) antes de
907
- * desistir.
908
- *
909
- * @returns {{
910
- * decision: 'RESPONDER'|'REANALISAR'|'UNKNOWN',
911
- * confidence: number,
912
- * margem: number,
913
- * texto: string | null,
914
- * unificado: boolean,
915
- * reanalisado: boolean,
916
- * melhor: {text: string, score: number, index: number} | null,
917
- * segundo: {text: string, score: number, index: number} | null
918
- * }}
919
- */
920
- analyze(message) {
921
- const vazio = { decision: "UNKNOWN", confidence: 0, margem: 0, texto: null, unificado: false, reanalisado: false, melhor: null, segundo: null };
922
- if (this.totalDocs === 0) return vazio;
923
- const ranking = this._rankTodos(message);
924
- if (ranking.length === 0 || ranking[0].score <= 0) return vazio;
925
- const melhor = ranking[0];
926
- const segundo = ranking[1] || { index: -1, score: 0 };
927
- const margem = (melhor.score - segundo.score) / melhor.score;
928
- const confidence = melhor.score / (melhor.score + 3);
929
- const melhorInfo = { text: this.blocks[melhor.index], score: melhor.score, index: melhor.index };
930
- const segundoInfo = segundo.index >= 0 ? { text: this.blocks[segundo.index], score: segundo.score, index: segundo.index } : null;
931
- if (confidence < CONFIANCA_MINIMA) return vazio;
932
- if (confidence >= CONFIANCA_ALTA && margem >= MARGEM_MINIMA) {
933
- return {
934
- decision: "RESPONDER",
935
- confidence,
936
- margem,
937
- texto: melhorInfo.text,
938
- unificado: false,
939
- reanalisado: false,
940
- melhor: melhorInfo,
941
- segundo: segundoInfo
942
- };
645
+ return 0 === s ? 0 : s / i.length;
646
+ }
647
+ _contemSequencia(e, t) {
648
+ outer: for (let i = 0; i + t.length <= e.length; i++) {
649
+ for (let o = 0; o < t.length; o++) if (e[i + o] !== t[o]) continue outer;
650
+ return true;
943
651
  }
944
- if (segundoInfo) {
945
- const similaridade = this._similaridadeBlocos(melhor.index, segundo.index);
946
- if (similaridade >= SIMILARIDADE_DUPLICADA) {
947
- return {
948
- decision: "RESPONDER",
949
- confidence,
950
- margem,
951
- texto: melhorInfo.text,
952
- unificado: false,
953
- reanalisado: false,
954
- melhor: melhorInfo,
955
- segundo: segundoInfo
956
- };
957
- }
958
- const termosPartilhados = this._termosPartilhados(melhor.index, segundo.index);
959
- if (termosPartilhados >= 1) {
960
- const resultado = this._tentarUnir(melhorInfo.text, segundoInfo.text);
961
- if (resultado && resultado.tipo === "duplicado") {
962
- return {
963
- decision: "RESPONDER",
964
- confidence,
965
- margem,
966
- texto: melhorInfo.text,
967
- unificado: false,
968
- reanalisado: false,
969
- melhor: melhorInfo,
970
- segundo: segundoInfo
971
- };
972
- }
973
- if (resultado && resultado.tipo === "unido") {
974
- return {
975
- decision: "RESPONDER",
976
- confidence,
977
- margem,
978
- texto: resultado.texto,
979
- unificado: true,
980
- reanalisado: false,
981
- melhor: melhorInfo,
982
- segundo: segundoInfo
983
- };
984
- }
652
+ return false;
653
+ }
654
+ _bonusTitulo(e, t) {
655
+ if (0 === e.length) return 0;
656
+ let i = new Set(this.docPalavrasSignificativas[t].slice(0, 8)), o = new Set(e), s = 0;
657
+ for (let r of o) i.has(r) && s++;
658
+ return s / o.size;
659
+ }
660
+ _scoreDoc(e, t, i) {
661
+ let o = this._scoreBM25(t, i), s = this._bonusFrase(e, i), r = this._bonusTitulo(this._palavrasSignificativas(e), i);
662
+ return 1 * o + 4 * s + 3 * r;
663
+ }
664
+ _rankTodos(e) {
665
+ let t = this._aplicarSinonimos(tokenizar(e));
666
+ if (0 === t.length) return [];
667
+ let i = [];
668
+ for (let o = 0; o < this.totalDocs; o++) i.push({ index: o, score: this._scoreDoc(e, t, o) });
669
+ return i.sort((e2, t2) => t2.score - e2.score), i;
670
+ }
671
+ search(e) {
672
+ if (0 === this.totalDocs) return null;
673
+ let t = this._rankTodos(e);
674
+ if (0 === t.length) return null;
675
+ let i = t[0];
676
+ return i.score <= 0 ? null : { text: this.blocks[i.index], score: i.score, confidence: i.score / (i.score + 3), index: i.index };
677
+ }
678
+ _similaridadeBlocos(e, t) {
679
+ let i = new Set(this.docs[e]), o = new Set(this.docs[t]);
680
+ if (0 === i.size || 0 === o.size) return 0;
681
+ let s = 0;
682
+ for (let r of i) o.has(r) && s++;
683
+ let n = i.size + o.size - s;
684
+ return 0 === n ? 0 : s / n;
685
+ }
686
+ _termosPartilhados(e, t) {
687
+ let i = new Set(this.docs[e]), o = this.docs[t], s = 0;
688
+ for (let r of o) i.has(r) && s++;
689
+ return s;
690
+ }
691
+ _tentarUnir(e, t) {
692
+ let i = extrairNomesProprios(e), o = extrairNomesProprios(t);
693
+ if (1 !== i.length || 1 !== o.length) return null;
694
+ let s = i[0], r = o[0];
695
+ if (normalizar(s).toLowerCase() === normalizar(r).toLowerCase()) return { tipo: "duplicado" };
696
+ let n = e.trim().split(/\s+/)[0].replace(/[.,;:!?]+$/, "");
697
+ return { tipo: "unido", texto: `${n} em v\xE1rios lugares, como ${s} e ${r}.` };
698
+ }
699
+ _reanalisarFocado(e) {
700
+ let t = this._aplicarSinonimos(tokenizar(e));
701
+ if (t.length <= 2) return null;
702
+ let i = t.map((e2) => ({ termo: e2, idf: this._idf(e2) }));
703
+ i.sort((e2, t2) => t2.idf - e2.idf);
704
+ let o = i.slice(0, Math.max(1, Math.ceil(t.length / 2))).map((e2) => e2.termo), s = [];
705
+ for (let r = 0; r < this.totalDocs; r++) s.push({ index: r, score: this._scoreDoc(e, o, r) });
706
+ return s.sort((e2, t2) => t2.score - e2.score), s;
707
+ }
708
+ analyze(e) {
709
+ let t = { decision: "UNKNOWN", confidence: 0, margem: 0, texto: null, unificado: false, reanalisado: false, melhor: null, segundo: null };
710
+ if (0 === this.totalDocs) return t;
711
+ let i = this._rankTodos(e);
712
+ if (0 === i.length || i[0].score <= 0) return t;
713
+ let o = i[0], s = i[1] || { index: -1, score: 0 }, r = (o.score - s.score) / o.score, n = o.score / (o.score + 3), a = { text: this.blocks[o.index], score: o.score, index: o.index }, l = s.index >= 0 ? { text: this.blocks[s.index], score: s.score, index: s.index } : null;
714
+ if (n < 0.15) return t;
715
+ if (n >= 0.55 && r >= 0.12) return { decision: "RESPONDER", confidence: n, margem: r, texto: a.text, unificado: false, reanalisado: false, melhor: a, segundo: l };
716
+ if (l) {
717
+ let c = this._similaridadeBlocos(o.index, s.index);
718
+ if (c >= 0.75) return { decision: "RESPONDER", confidence: n, margem: r, texto: a.text, unificado: false, reanalisado: false, melhor: a, segundo: l };
719
+ let d = this._termosPartilhados(o.index, s.index);
720
+ if (d >= 1) {
721
+ let h = this._tentarUnir(a.text, l.text);
722
+ if (h && "duplicado" === h.tipo) return { decision: "RESPONDER", confidence: n, margem: r, texto: a.text, unificado: false, reanalisado: false, melhor: a, segundo: l };
723
+ if (h && "unido" === h.tipo) return { decision: "RESPONDER", confidence: n, margem: r, texto: h.texto, unificado: true, reanalisado: false, melhor: a, segundo: l };
985
724
  }
986
725
  }
987
- const tentativa2 = this._reanalisarFocado(message);
988
- if (tentativa2 && tentativa2.length > 0 && tentativa2[0].score > 0) {
989
- const melhor2 = tentativa2[0];
990
- const segundo2 = tentativa2[1] || { index: -1, score: 0 };
991
- const margem2 = (melhor2.score - segundo2.score) / melhor2.score;
992
- const confidence2 = melhor2.score / (melhor2.score + 3);
993
- if (confidence2 >= CONFIANCA_ALTA && margem2 >= MARGEM_MINIMA) {
994
- return {
995
- decision: "RESPONDER",
996
- confidence: confidence2,
997
- margem: margem2,
998
- texto: this.blocks[melhor2.index],
999
- unificado: false,
1000
- reanalisado: true,
1001
- melhor: { text: this.blocks[melhor2.index], score: melhor2.score, index: melhor2.index },
1002
- segundo: segundo2.index >= 0 ? { text: this.blocks[segundo2.index], score: segundo2.score, index: segundo2.index } : null
1003
- };
1004
- }
726
+ let u = this._reanalisarFocado(e);
727
+ if (u && u.length > 0 && u[0].score > 0) {
728
+ let f = u[0], $ = u[1] || { index: -1, score: 0 }, m = (f.score - $.score) / f.score, g = f.score / (f.score + 3);
729
+ if (g >= 0.55 && m >= 0.12) return { decision: "RESPONDER", confidence: g, margem: m, texto: this.blocks[f.index], unificado: false, reanalisado: true, melhor: { text: this.blocks[f.index], score: f.score, index: f.index }, segundo: $.index >= 0 ? { text: this.blocks[$.index], score: $.score, index: $.index } : null };
1005
730
  }
1006
- return {
1007
- decision: "REANALISAR",
1008
- confidence,
1009
- margem,
1010
- texto: null,
1011
- unificado: false,
1012
- reanalisado: false,
1013
- melhor: melhorInfo,
1014
- segundo: segundoInfo
1015
- };
731
+ return { decision: "REANALISAR", confidence: n, margem: r, texto: null, unificado: false, reanalisado: false, melhor: a, segundo: l };
1016
732
  }
1017
733
  };
1018
734
  var KnowledgeCache = class {
1019
- constructor(fileSystem) {
1020
- this.fileSystem = fileSystem;
1021
- this.cache = /* @__PURE__ */ new Map();
1022
- }
1023
- get(resolvedPath, options) {
1024
- if (this.cache.has(resolvedPath)) return this.cache.get(resolvedPath);
1025
- if (!this.fileSystem.exists(resolvedPath)) {
1026
- throw new Error(`BotQL/THINK: ficheiro de conhecimento n\xE3o encontrado: "${resolvedPath}"`);
1027
- }
1028
- const texto = this.fileSystem.readFile(resolvedPath);
1029
- const index = new KnowledgeIndex(texto, options);
1030
- this.cache.set(resolvedPath, index);
1031
- return index;
735
+ constructor(e) {
736
+ this.fileSystem = e, this.cache = /* @__PURE__ */ new Map();
737
+ }
738
+ get(e, t) {
739
+ if (this.cache.has(e)) return this.cache.get(e);
740
+ if (!this.fileSystem.exists(e)) throw Error(`BotQL/THINK: ficheiro de conhecimento n\xE3o encontrado: "${e}"`);
741
+ let i = this.fileSystem.readFile(e), o = new KnowledgeIndex(i, t);
742
+ return this.cache.set(e, o), o;
1032
743
  }
1033
744
  };
1034
745
  module.exports = { KnowledgeIndex, KnowledgeCache, tokenizar, normalizar, stem, distanciaEdicao };
@@ -1042,10 +753,9 @@ var BotQL = (() => {
1042
753
  var { MemoryDatabase: t } = require_Database();
1043
754
  var { createDefaultFileSystem: s } = require_FileSystem();
1044
755
  var { KnowledgeCache: i } = require_RAG();
1045
- var WAITING_TEXTO_PADRAO = "Pensando...";
1046
756
  var BotQLInterpreter = class _BotQLInterpreter {
1047
757
  constructor(e2 = {}) {
1048
- this.db = e2.db || new t(), this.onReply = e2.onReply || null, this.onForward = e2.onForward || null, this.onSend = e2.onSend || null, this.signalParser = e2.signalParser || ((e3) => e3), this.onThinking = e2.onThinking || null, this.botName = null, this.platform = null, this.connections = [], this.handlers = { START: [], MESSAGE: [], SIGNAL: [] }, this.running = false, this.responseConnections = /* @__PURE__ */ new Set(), this.onResponse = e2.onResponse || null, this.defaultMessageConfig = null, this.nativeFuncs = /* @__PURE__ */ new Map(), this.registerFunction("NOW", () => (/* @__PURE__ */ new Date()).toISOString()), this.fileSystem = e2.fileSystem || s();
758
+ this.db = e2.db || new t(), this.onReply = e2.onReply || null, this.onForward = e2.onForward || null, this.onSend = e2.onSend || null, this.signalParser = e2.signalParser || ((e3) => e3), this.onThinking = e2.onThinking || null, this.botName = null, this.platform = null, this.connections = [], this.handlers = { START: [], MESSAGE: [], SIGNAL: [] }, this.running = false, this.responseConnections = /* @__PURE__ */ new Set(), this._clientesOcupados = /* @__PURE__ */ new Set(), this.onBusy = e2.onBusy || null, this.onResponse = e2.onResponse || null, this.defaultMessageConfig = null, this.nativeFuncs = /* @__PURE__ */ new Map(), this.registerFunction("NOW", () => (/* @__PURE__ */ new Date()).toISOString()), this.fileSystem = e2.fileSystem || s();
1049
759
  let n = "undefined" != typeof process && process.cwd ? process.cwd() : "";
1050
760
  this._basePathStack = [e2.basePath || n], this._importedFiles = /* @__PURE__ */ new Set(), this._rootBasePath = e2.basePath || n, this._keywordsFileCache = /* @__PURE__ */ new Map(), this._replyFileCache = /* @__PURE__ */ new Map(), this.envValues = /* @__PURE__ */ new Map(), this.knowledgeCache = new i(this.fileSystem);
1051
761
  }
@@ -1111,8 +821,8 @@ var BotQL = (() => {
1111
821
  if (this._importedFiles.has(u)) continue;
1112
822
  if (!this.fileSystem.exists(u)) throw Error(`runtime error: IMPORT failed, file not found: "${u}"`);
1113
823
  this._importedFiles.add(u);
1114
- let c = this.fileSystem.readFile(u), f = new e(c).parseProgram(), d = this._flattenImports(f.body, this.fileSystem.dirname(u));
1115
- i2.push(...d);
824
+ let c = this.fileSystem.readFile(u), d = new e(c).parseProgram(), f = this._flattenImports(d.body, this.fileSystem.dirname(u));
825
+ i2.push(...f);
1116
826
  }
1117
827
  return [...i2, ...n];
1118
828
  }
@@ -1126,10 +836,16 @@ var BotQL = (() => {
1126
836
  }
1127
837
  }
1128
838
  async receiveMessage(e2, t2) {
1129
- let s2 = this.createContext({ client: e2, message: t2 }), i2 = await this.greetIfNew(e2);
1130
- if (i2) return s2;
1131
- for (let n of this.handlers.MESSAGE) await this.run(n.body, s2);
1132
- return s2;
839
+ if (this._clientesOcupados.has(e2)) return this.onBusy && await this.onBusy({ client: e2, message: t2 }), null;
840
+ this._clientesOcupados.add(e2);
841
+ try {
842
+ let s2 = this.createContext({ client: e2, message: t2 }), i2 = await this.greetIfNew(e2);
843
+ if (i2) return s2;
844
+ for (let n of this.handlers.MESSAGE) await this.run(n.body, s2);
845
+ return s2;
846
+ } finally {
847
+ this._clientesOcupados.delete(e2);
848
+ }
1133
849
  }
1134
850
  async greetIfNew(e2) {
1135
851
  if (!this.defaultMessageConfig) return false;
@@ -1182,9 +898,10 @@ var BotQL = (() => {
1182
898
  return this._keywordsFileCache.set(e2, i2), i2;
1183
899
  }
1184
900
  resolveWaitingConfig(e2, t2) {
1185
- if (!e2) return { text: WAITING_TEXTO_PADRAO, seconds: 3 };
1186
- let s2 = WAITING_TEXTO_PADRAO;
1187
- if (e2.file) {
901
+ if (!e2) return { text: null, seconds: 3 };
902
+ let s2 = null;
903
+ if (null !== e2.text && void 0 !== e2.text) s2 = e2.text;
904
+ else if (e2.file) {
1188
905
  let i2 = this.fileSystem.resolve(this._rootBasePath, e2.file);
1189
906
  if (!this.fileSystem.exists(i2)) throw Error(`runtime error: WAITING failed, file not found: "${i2}"`);
1190
907
  s2 = this.fileSystem.readFile(i2).trim();
@@ -1217,11 +934,11 @@ var BotQL = (() => {
1217
934
  }
1218
935
  let h = Number(l[0]), u = a + l[0].length + 1;
1219
936
  if ("{" === e2[u]) {
1220
- let c = 1, f = u + 1, d = f;
1221
- for (; d < i2 && c > 0; ) "{" === e2[d] ? c++ : "}" === e2[d] && c--, d++;
937
+ let c = 1, d = u + 1, f = d;
938
+ for (; f < i2 && c > 0; ) "{" === e2[f] ? c++ : "}" === e2[f] && c--, f++;
1222
939
  if (0 !== c) throw Error(`runtime error: unclosed "{" for entry ${h} in "${t2}"`);
1223
- let p = e2.slice(f, d - 1).trim();
1224
- s2.set(h, p), n = d;
940
+ let p = e2.slice(d, f - 1).trim();
941
+ s2.set(h, p), n = f;
1225
942
  continue;
1226
943
  }
1227
944
  let m = e2.indexOf("\n", u), w = -1 === m ? i2 : m, g = e2.slice(u, w).trim();
@@ -1236,19 +953,24 @@ var BotQL = (() => {
1236
953
  t2.vars.lastMsg = s2, this.onReply && await this.onReply({ target: e2.target, text: s2, client: t2.vars.client });
1237
954
  break;
1238
955
  }
956
+ case "Waiting": {
957
+ let i2 = this.resolveWaitingConfig(e2, t2);
958
+ this.onThinking && await this.onThinking({ client: t2.vars.client, text: i2.text }), await new Promise((e3) => setTimeout(e3, 1e3 * i2.seconds));
959
+ break;
960
+ }
1239
961
  case "Think": {
1240
- let i2 = this.resolveWaitingConfig(e2.waiting, t2), n = Date.now();
1241
- this.onThinking && await this.onThinking({ client: t2.vars.client, text: i2.text });
1242
- let r = this.fileSystem.resolve(this._rootBasePath, e2.file), a = this.knowledgeCache.get(r), l = a.analyze(t2.vars.message), o = Date.now() - n, h = 1e3 * i2.seconds - o;
1243
- if (h > 0 && await new Promise((e3) => setTimeout(e3, h)), "RESPONDER" === l.decision) {
1244
- let u = l.texto;
1245
- t2.vars.lastMsg = u, this.onReply && await this.onReply({ target: null, text: u, client: t2.vars.client });
962
+ let n = this.resolveWaitingConfig(e2.waiting, t2), r = Date.now();
963
+ this.onThinking && await this.onThinking({ client: t2.vars.client, text: n.text });
964
+ let a = this.fileSystem.resolve(this._rootBasePath, e2.file), l = this.knowledgeCache.get(a), o = l.analyze(t2.vars.message), h = Date.now() - r, u = 1e3 * n.seconds - h;
965
+ if (u > 0 && await new Promise((e3) => setTimeout(e3, u)), "RESPONDER" === o.decision) {
966
+ let c = o.texto;
967
+ t2.vars.lastMsg = c, this.onReply && await this.onReply({ target: null, text: c, client: t2.vars.client });
1246
968
  } else e2.fallback && await this.execStatement(e2.fallback, t2);
1247
969
  break;
1248
970
  }
1249
971
  case "ForwardTo": {
1250
- let c = String(this.evalExpr(e2.target, t2));
1251
- this.onForward && await this.onForward({ target: c, client: t2.vars.client });
972
+ let d = String(this.evalExpr(e2.target, t2));
973
+ this.onForward && await this.onForward({ target: d, client: t2.vars.client });
1252
974
  break;
1253
975
  }
1254
976
  case "ParseSignal":