Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

362
LINES

< > BotCompany Repo | #1026409 // Tomii Boi Answers DB [old]

JavaX module (desktop) [tags: butter use-pretranspiled] - homepage

Download Jar. Libraryless. Click here for Pure Java version (20225L/146K).

1  
!7
2  
3  
sS mainBotID = #1026411;
4  
static int maxTypos = 1;
5  
6  
sS adminTitle = "Tomii Boi Chat Bot Admin";
7  
sS shortLink = "https://botcompany.de/tomii-boi/admin"; // catchy link to admin if available
8  
9  
sS embedderLink; // where chat bot will go
10  
sS goDaddyEmbedCode;
11  
12  
static Cache<Scorer<ConsistencyError>> consistencyCheckResult = new(lambda0 consistencyCheck);
13  
14  
concept Category {
15  
  int index;
16  
  S name;
17  
}
18  
19  
concept Language {
20  
  int index;
21  
  S code;
22  
}
23  
24  
concept QA {
25  
  int index;
26  
  S language;
27  
  S questions; // line by line
28  
  S patterns; // to be determined
29  
  S answers; // one answer, or "random { answers separated by empty lines }"
30  
  S category; // "business", "smalltalk"
31  
32  
  transient MMOPattern parsedPattern;
33  
34  
  sS _fieldOrder = "language category index questions patterns answers";
35  
  
36  
  void change {
37  
    parsedPattern = null;
38  
    super.change();
39  
  }
40  
  
41  
  MMOPattern parsedPattern() {
42  
    if (parsedPattern == null)
43  
      parsedPattern = mmo_parsePattern(patterns);
44  
    ret parsedPattern;
45  
  }
46  
}
47  
48  
concept Defaults {
49  
  S lastLanguage;
50  
}
51  
52  
concept Settings {
53  
  S contactFormViberID;
54  
  bool botOn, botAutoOpen;
55  
}
56  
57  
p {
58  
  print("fieldOrder", getFieldOrder(QA));
59  
  dbIndexing(Language, 'index, Category, 'index);
60  
  indexSingletonConcept(Defaults);
61  
  indexSingletonConcept(Settings);
62  
  
63  
  // languages, default language
64  
  cset(conceptsWhere QA(language := null), language := 'en);
65  
  uniq(Language, code := 'en);
66  
  removeConceptsWhere(Language, code := 'de);
67  
  
68  
  // categories, default category
69  
  int i = 0;
70  
  for (S category : ll("business", "smalltalk"))
71  
    cset(uniq(Category, name := category), index := i++);
72  
  cset(conceptsWhere QA(category := null), category := "business");
73  
  
74  
  onConceptsChange(r { consistencyCheckResult.clear(); });
75  
  reindexQAs();
76  
}
77  
78  
html { try {
79  
  if (swic(addSlash(uri), "/answer/")) {
80  
    temp tempSetTL(opt_noDefault, valueIs1 noDefault(params));
81  
    ret serveText(unnull(answer(params.get("q"), "en")));
82  
  }
83  
    
84  
  // force auth
85  
  try answer callHtmlMethod(getBot(mainBotID), "/auth-only", mapPlus(
86  
params, uri := rawLink(uri)));
87  
    
88  
  if (eq(uri, "/demo"))
89  
    ret hrefresh(appendQueryToURL(rawBotLink(mainBotID), _botDemo := 1, bot := 1));
90  
    
91  
  if (eq(uri, "/download"))
92  
    ret subBot_serveText(conceptsStructure());
93  
    
94  
  if (eq(uri, "/embedCode"))
95  
    ret htitle_h2("Chat bot embed code")
96  
      + (empty(goDaddyEmbedCode) ? "" :
97  
        h3("GoDaddy Site Builder")
98  
      
99  
        + p("Add a HTML box with this code:")
100  
      
101  
        + pre(htmlEncode2(goDaddyEmbedCode))
102  
        
103  
        + h3("Other"))
104  
        
105  
      + p("Add this code in front of your " + tt(htmlEncode2("</body>")) + " tag:")
106  
      
107  
      + pre(htmlEncode2(hjavascript_src_withType(rawBotLink(mainBotID))));
108  
    
109  
  HCRUD_Concepts<QA> data = new HCRUD_Concepts<QA>(QA) {
110  
    S itemName() { ret "question"; }
111  
    
112  
    S fieldHelp(S field) {
113  
      if (eq(field, "index"))
114  
        ret "lower index = higher matching precedence";
115  
      if (eq(field, "patterns"))
116  
        ret [[Phrases to match. Use "+" as "and" operator, commas as "or" operator. Round brackets for grouping. Quotes for special characters. Put exclamation mark after phrase to disable typo correction.]];
117  
      if (eq(field, "answers"))
118  
        ret "Text of answer. Use random { ... } for multiple answers (separated from each other by an empty line)";
119  
      null;
120  
    }
121  
    
122  
    Renderer getRenderer(S field) {
123  
      if (eqOneOf(field, 'questions, 'answers))
124  
        ret new TextArea(80, 10);
125  
      if (eq(field, 'patterns))
126  
        ret new TextField(80);
127  
      if (eq(field, 'language))
128  
        ret new ComboBox(collect code(conceptsSortedByField(Language, 'index)));
129  
      if (eq(field, 'category))
130  
        ret new ComboBox(collect name(conceptsSortedByField(Category, 'index)));
131  
      ret super.getRenderer(field);
132  
    }
133  
    
134  
    // sort by language first, then by priority (category + index)
135  
    L<QA> defaultSort(L<QA> l) {
136  
      ret sortByFieldDesc language(sortQAs(l));
137  
    }
138  
    
139  
    Map<S, O> emptyObject() {
140  
      ret mapPlus(super.emptyObject(), language := uniq(Defaults).lastLanguage);
141  
    }
142  
    
143  
    O createObject(SS map) {
144  
      S language = cast map.get('language);
145  
      if (language != null) cset(uniq(Defaults), lastLanguage := language);
146  
      ret super.createObject(map);
147  
    }
148  
  };
149  
  data.onCreateOrUpdate.add(qa -> reindexQAs());
150  
  
151  
  HCRUD crud = new(rawLink(), data) {
152  
    S frame(S title, S contents) {
153  
      title = ahref(or2(shortLink, rawLink("")), adminTitle) + " | " + title;
154  
      ret hhtml(hhead_title_decode(title)
155  
        + hbody(h2(title)
156  
        + p(joinWithVBar(
157  
          ahref("?logout=1", "log out"),
158  
          targetBlank(relativeRawBotLink(mainBotID, "/logs"), "chat logs"),
159  
          targetBlank(rawLink("demo?bot=1"), "demo"),
160  
          targetBlank(rawLink("download"), "export brain"),
161  
          ahref(rawLink("embedCode"), "show embed code"),
162  
          botOn()
163  
            ? "Bot is ON (appears on home page) " + ahrefWithConfirm("Switch bot off?", "?botOff=1", "[switch bot off]")
164  
            : "Bot is OFF (appears only with " + targetBlank(appendQueryToURL(embedderLink, bot := 1), "?bot=1") + ") " + ahrefWithConfirm("Switch bot on?", "?botOn=1", "[switch bot on]")
165  
          ))
166  
        + contents));
167  
    }
168  
    
169  
    S renderTable(bool withCmds) {
170  
      Scorer<ConsistencyError> scorer = consistencyCheckResult!;
171  
      ret (empty(scorer.errors)
172  
        ? p("Pattern analysis: No problems found. " + nEntries(countConcepts(QA)) + ".")
173  
        : joinMap(scorer.errors, lambda1 renderErrorAsParagraph))
174  
        + super.renderTable(withCmds);
175  
    }
176  
    
177  
    S renderErrorAsParagraph(ConsistencyError error) {
178  
      S fix = error.renderFix();
179  
      ret p("Error: " + htmlEncode2(error.msg) + " " +
180  
        joinMap(error.items, qa -> ahref(editLink(qa.id), "[item]"))
181  
        + (empty(fix) ? "" : " " + fix));
182  
    }
183  
  };
184  
  
185  
  // actions
186  
187  
  if (eqGet(params, action := 'reindex)) {
188  
    long id = parseLong(params.get('id));
189  
    int newIndex = parseInt(params.get('newIndex));
190  
    QA qa = getConcept(QA, id);
191  
    if (qa == null) ret crud.refreshWithMsgs("Item not found");
192  
    cset(qa, index := newIndex);
193  
    reindexQAs();
194  
    ret crud.refreshWithMsgs("Index of item " + qa.id + " changed");
195  
  }
196  
197  
  if (eqGet(params, botOn := "1")) {
198  
    cset(uniq(Settings), botOn := true);
199  
    ret crud.refreshWithMsgs("Bot turned on!");
200  
  }
201  
  
202  
  if (eqGet(params, botOff := "1")) {
203  
    cset(uniq(Settings), botOn := false);
204  
    ret crud.refreshWithMsgs("Bot turned off!");
205  
  }
206  
  
207  
  if (nemptyGet botAutoOpen(params)) {
208  
    cset(uniq(Settings), botAutoOpen := eq("1", params.get("botAutoOpen")));
209  
    ret crud.refreshWithMsgs("Bot auto-open turned " + onOff(botAutoOpen()) + "!");
210  
  }
211  
  
212  
  ret crud.renderPage(params);
213  
 } catch print e {
214  
  ret "ERROR.";
215  
 }
216  
}
217  
218  
static new ThreadLocal<S> language_out;
219  
static new ThreadLocal<Bool> opt_noDefault; // true = return null instead of #default text
220  
221  
// main API function for other bots
222  
sS answer(S s, S language) {
223  
  language_out.set(null);
224  
  ret trim(answer_main(s, language));
225  
}
226  
227  
sS answer_main(S s, S language) {
228  
  S raw = findRawAnswer(s, language, true);
229  
  S contents = extractKeywordPlusBracketed_keepComments("random", raw);
230  
  if (contents != null)
231  
    ret random(splitAtEmptyLines(contents));
232  
  ret raw;
233  
}
234  
235  
sS findRawAnswer(S s, S language, bool allowTypos) {
236  
  ret selectQA(findMatchingQA(s, language, allowTypos));
237  
}
238  
239  
static QA findQAWithTypos(S s, S language) {
240  
  Lowest<QA> qa = findClosestQA(s, language);
241  
  if (!qa.has()) null;
242  
  if (qa.score() > maxTypos) {
243  
    print("Rejecting " + nTypos((int) qa.score()) + ": " + s + " / " + qa->patterns);
244  
    null;
245  
  }
246  
  print("Accepting " + nTypos((int) qa.score()) + ": " + s + " / " + qa->patterns);
247  
  ret qa!;
248  
}
249  
250  
static QA findMatchingQA(S s, S language, bool allowTypos) {
251  
  try object QA qa = findMatchingQA(s, conceptsWhere(QA, +language));
252  
  try object QA qa = findMatchingQA(s, filter(list(QA), q -> neq(q.language, language)));
253  
  if (allowTypos)
254  
    try object QA qa = findQAWithTypos(s, language);
255  
  if (!eq(s, "#default") && !isTrue(opt_noDefault!)) ret findMatchingQA("#default", language, false);
256  
  null;
257  
}
258  
259  
static Lowest<QA> findClosestQA(S s, S language) {
260  
  time "findClosestQA" {
261  
    new Lowest<QA> qa;
262  
    findClosestQA(s, qa, conceptsWhere(QA, +language));
263  
    findClosestQA(s, qa, filter(list(QA), q -> neq(q.language, language)));
264  
  }
265  
  ret qa;
266  
}
267  
268  
static L<QA> sortQAs(Cl<QA> qas) {
269  
  Map<S, Int> categoryToIndex = fieldToFieldIndex('name, 'index, list(Category));
270  
  ret sortedByCalculatedField(qas, q -> pair(categoryToIndex.get(q.category), q.index));
271  
}
272  
273  
svoid reindexQAs() {
274  
  for (Language lang) {
275  
    int index = 1;
276  
    for (QA qa : sortQAs(conceptsWhere(QA, language := lang.code)))
277  
      cset(qa, index := index++);
278  
  }
279  
}
280  
281  
static QA findMatchingQA(S s, Cl<QA> qas) {
282  
  for (QA qa : sortQAs(qas))
283  
    if (mmo_match_parsedPattern(qa.parsedPattern(), s))
284  
      ret qa;
285  
  null;
286  
}
287  
288  
svoid findClosestQA(S s, Lowest<QA> best, Cl<QA> qas) {
289  
  for (QA qa : sortQAs(qas)) {
290  
    Int score = mmo_levenWithSwapsScore_parsedPattern(qa.parsedPattern(), s);
291  
    if (score != null)
292  
      best.put(qa, score);
293  
  }
294  
}
295  
296  
// returns answers
297  
sS selectQA(QA qa) {
298  
  if (qa == null) null;
299  
  language_out.set(qa.language);
300  
  ret qa.answers;
301  
}
302  
303  
sclass ConsistencyError {
304  
  S msg;
305  
  L<QA> items;
306  
  
307  
  *(S *msg, QA... items) { this.items = asList(items); }
308  
  
309  
  S renderFix() { null; }
310  
}
311  
312  
svoid checkLocalConsistency(QA qa, Scorer<ConsistencyError> scorer) {
313  
  LS questions = tlft(qa.questions);
314  
  for (S q : questions)
315  
    if (mmo_match_parsedPattern(qa.parsedPattern(), q))
316  
      scorer.ok();
317  
    else
318  
      scorer.error(ConsistencyError("Question " + quote(q) + " not matched by patterns " + quote(qa.patterns), qa));
319  
}
320  
321  
svoid checkGlobalConsistency(QA qa, Scorer<ConsistencyError> scorer) {
322  
  for (S q : tlft(qa.questions)) {
323  
    QA found = findMatchingQA(q, qa.language, false);
324  
    if (found != null && found != qa)
325  
      scorer.error(new ConsistencyError("Question " + quote(q) + " (item " + qa.id + ") shadowed by patterns " + quote(found.patterns) + " (item " + found.id + ")", qa, found) {
326  
        S renderFix() {
327  
          ret ahref(rawLink("?action=reindex&id=" + qa.id + "&newIndex=" + (found.index-1)), "[fix by changing index]");
328  
        }
329  
      });
330  
    else
331  
      scorer.ok();
332  
  }
333  
}
334  
335  
static Scorer<ConsistencyError> consistencyCheck() {
336  
  new Scorer<ConsistencyError> scorer;
337  
  scorer.collectErrors();
338  
  for (QA qa) {
339  
    checkLocalConsistency(qa, scorer);
340  
    checkGlobalConsistency(qa, scorer);
341  
  }
342  
  ret scorer;
343  
}
344  
345  
sS contactFormViberID() {
346  
  ret uniq(Settings).contactFormViberID;
347  
}
348  
349  
// API
350  
351  
sbool botOn() {
352  
  ret uniq(Settings).botOn;
353  
}
354  
355  
sbool botAutoOpen() {
356  
  ret uniq(Settings).botAutoOpen;
357  
}
358  
359  
svoid importQA(virtual QA qa_external) {
360  
  QA qa = shallowCloneToUnlistedConcept QA(qa_external);
361  
  uniq QA(allConceptFieldsAsParams(qa));
362  
}

Author comment

Began life as a copy of #1026223

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1026409
Snippet name: Tomii Boi Answers DB [old]
Eternal ID of this version: #1026409/27
Text MD5: c7aa1272ba1f25814c7925c408afbe0a
Transpilation MD5: 1d9485245fda0f83ff29d2a33f63fb09
Author: stefan
Category: javax / html
Type: JavaX module (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-05-05 23:46:30
Source code size: 11207 bytes / 362 lines
Pitched / IR pitched: No / No
Views / Downloads: 314 / 1999
Version history: 26 change(s)
Referenced in: [show references]