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

451
LINES

< > BotCompany Repo | #1028043 // Tomii Boi Answers DB Module backup 1

JavaX source code (Dynamic Module) [tags: use-pretranspiled] - run with: Stefan's OS

Uses 911K of libraries. Click here for Pure Java version (23262L/132K).

1  
!7
2  
3  
cmodule TomiiBoiAnswers > DynPrintLogAndEnabled {
4  
  switchable int port = 8080;
5  
  switchable S frontendName = "tomiiBoiDiscordBot";
6  
  
7  
  transient CRUD<Category> categoriesCRUD;
8  
  transient CRUD<Server> serversCRUD;
9  
  transient CRUD<Channel> channelsCRUD;
10  
  transient CRUD<ServerToCategory> serverToCategoryCRUD;
11  
12  
  start {
13  
    init();
14  
    categoriesCRUD = new CRUD(Category);
15  
    serversCRUD = new CRUD(Server);
16  
    serverToCategoryCRUD = new CRUD(ServerToCategory);
17  
    channelsCRUD = new CRUD(Channel);
18  
19  
    thread {
20  
      dm_serveHttpFromFunction(port, lambda2 html);
21  
      print("Admin live at: http://localhost:" + port);
22  
      dm_registerAs('tomiiBoiQA);
23  
    }
24  
  }
25  
  
26  
  visualize {
27  
    JComponent c = super.visualize();
28  
    addComponents(buttons,
29  
      jbutton("Open admin in browser", rThread { openURLInBrowser("http://localhost:" + port) }),
30  
      jPopDownButton_noText(
31  
        "Import data...", rThreadEnter importData));
32  
    c = jtabs(
33  
      "Main", c,
34  
      "Categories", categoriesCRUD.visualize(),
35  
      "Servers", serversCRUD.visualize(),
36  
      "Server-to-category", serverToCategoryCRUD.visualize(),
37  
      "Channels", channelsCRUD.visualize());
38  
    channelsCRUD.addButton("Update list", rThreadEnter grabChannels);
39  
    ret c;
40  
  }
41  
  
42  
  void importData {
43  
    selectFile("Tomii Brain File", voidfunc(File f) enter {
44  
      replaceConceptsWithTextFileOnNextStart(f);
45  
      dm_reloadModule();
46  
    });
47  
  }
48  
49  
  void grabChannels {
50  
    dm_call(frontendName, 'grabChannels);
51  
  }
52  
53  
  // API
54  
55  
  Server addServer(S serverID, S name) {
56  
    ret csetAndReturn(uniq Server(+serverID), +name);
57  
  }
58  
59  
  Channel addChannel(Server server, S channelID, S name) {
60  
    ret csetAndReturn(uniq Channel(+server, +channelID), +name);
61  
  }
62  
63  
  Channel channelForID(S channelID) {
64  
    ret conceptWhere Channel(+channelID);
65  
  }
66  
67  
  Cl<S> categoriesForServer(Server server) {
68  
    Set<Category> set = asSet(conceptsWhere Category(onByDefault := true));
69  
    for (ServerToCategory link : conceptsWhere(ServerToCategory, +server))
70  
      addOrRemove(set, link.category, link.enabled);
71  
    ret collectAsSet name(set);
72  
  }
73  
}
74  
75  
//sS mainBotID = #1026411;
76  
static int maxTypos = 1;
77  
78  
sS adminTitle = "Tomii Boi Chat Bot Admin";
79  
sS shortLink; // catchy link to admin if available
80  
81  
sS embedderLink; // where chat bot will go
82  
sS goDaddyEmbedCode;
83  
84  
static Cache<Scorer<ConsistencyError>> consistencyCheckResult = new(lambda0 consistencyCheck);
85  
86  
concept Server {
87  
  S serverID, name;
88  
89  
  toString { ret name; }
90  
}
91  
92  
concept ServerToCategory {
93  
  Server server;
94  
  Category category;
95  
  bool enabled;
96  
97  
  sS _fieldOrder = "server category enabled";
98  
}
99  
100  
concept Channel {
101  
  Server server;
102  
  S channelID;
103  
  S name;
104  
  bool botEnabled = true;
105  
106  
  toString { ret name; }
107  
  sS _fieldOrder = "botEnabled name server";
108  
}
109  
110  
concept Category {
111  
  int index;
112  
  S name;
113  
  bool onByDefault = true;
114  
115  
  toString { ret name; }
116  
}
117  
118  
concept Language {
119  
  int index;
120  
  S code;
121  
}
122  
123  
concept QA {
124  
  int index;
125  
  S language;
126  
  S questions; // line by line
127  
  S patterns; // to be determined
128  
  S answers; // one answer, or "random { answers separated by empty lines }"
129  
  S category; // "business", "smalltalk"
130  
131  
  transient MMOPattern parsedPattern;
132  
133  
  sS _fieldOrder = "language category index questions patterns answers";
134  
  
135  
  void change {
136  
    parsedPattern = null;
137  
    super.change();
138  
  }
139  
  
140  
  MMOPattern parsedPattern() {
141  
    if (parsedPattern == null)
142  
      parsedPattern = mmo_parsePattern(patterns);
143  
    ret parsedPattern;
144  
  }
145  
}
146  
147  
concept Defaults {
148  
  S lastLanguage;
149  
}
150  
151  
concept Settings {
152  
  S contactFormViberID;
153  
  bool botOn, botAutoOpen;
154  
}
155  
156  
svoid init {
157  
  processConceptsOverwriteFile();
158  
  //print("fieldOrder", getFieldOrder(QA));
159  
  dbIndexing(Language, 'index, Category, 'index, Channel, 'channelID);
160  
  print("QA count: " + countConcepts(QA));
161  
  indexSingletonConcept(Defaults);
162  
  indexSingletonConcept(Settings);
163  
  
164  
  // languages, default language
165  
  cset(conceptsWhere QA(language := null), language := 'en);
166  
  uniq(Language, code := 'en);
167  
  removeConceptsWhere(Language, code := 'de);
168  
  
169  
  // categories, default category
170  
  int i = 0;
171  
  for (S category : ll("business", "smalltalk", "btd"))
172  
    cset(uniq(Category, name := category), index := i++);
173  
  cset(conceptsWhere QA(category := null), category := "business");
174  
  
175  
  onConceptsChange(r { consistencyCheckResult.clear(); });
176  
  reindexQAs();
177  
}
178  
179  
html { try {
180  
  print(+uri);
181  
  if (swic(addSlash(uri), "/answer/")) {
182  
    temp tempSetTL(opt_noDefault, valueIs1 noDefault(params));
183  
    ret serveText(unnull(answer(params.get("q"), "en")));
184  
  }
185  
    
186  
  // force auth
187  
  /*try answer callHtmlMethod(getBot(mainBotID), "/auth-only", mapPlus(
188  
params, uri := rawLink(uri)));*/
189  
    
190  
  if (eq(uri, "/download"))
191  
    ret serveText(conceptsStructure());
192  
    
193  
  HCRUD_Concepts<QA> data = new HCRUD_Concepts<QA>(QA) {
194  
    S itemName() { ret "question"; }
195  
    
196  
    S fieldHelp(S field) {
197  
      if (eq(field, "index"))
198  
        ret "lower index = higher matching precedence";
199  
      if (eq(field, "patterns"))
200  
        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.]];
201  
      if (eq(field, "answers"))
202  
        ret "Text of answer. Use random { ... } for multiple answers (separated from each other by an empty line)";
203  
      null;
204  
    }
205  
    
206  
    Renderer getRenderer(S field) {
207  
      if (eqOneOf(field, 'questions, 'answers))
208  
        ret new TextArea(80, 10);
209  
      if (eq(field, 'patterns))
210  
        ret new TextField(80);
211  
      if (eq(field, 'language))
212  
        ret new ComboBox(collect code(conceptsSortedByField(Language, 'index)));
213  
      if (eq(field, 'category))
214  
        ret new ComboBox(collect name(conceptsSortedByField(Category, 'index)));
215  
      ret super.getRenderer(field);
216  
    }
217  
    
218  
    // sort by language first, then by priority (category + index)
219  
    L<QA> defaultSort(L<QA> l) {
220  
      ret sortByFieldDesc language(sortQAs(l));
221  
    }
222  
    
223  
    Map<S, O> emptyObject() {
224  
      ret mapPlus(super.emptyObject(), language := uniq(Defaults).lastLanguage);
225  
    }
226  
    
227  
    O createObject(SS map) {
228  
      S language = cast map.get('language);
229  
      if (language != null) cset(uniq(Defaults), lastLanguage := language);
230  
      ret super.createObject(map);
231  
    }
232  
  };
233  
  data.onCreateOrUpdate.add(qa -> reindexQAs());
234  
  
235  
  HCRUD crud = new(rawLink(), data) {
236  
    S frame(S title, S contents) {
237  
      title = ahref(or2(shortLink, rawLink("")), adminTitle) + " | " + title;
238  
      ret hhtml(hhead_title_decode(title)
239  
        + hbody(h2(title)
240  
        + p(joinWithVBar(
241  
          targetBlank(rawLink("download"), "export brain"),
242  
          ))
243  
        + contents));
244  
    }
245  
    
246  
    S renderTable(bool withCmds) {
247  
      Scorer<ConsistencyError> scorer = consistencyCheckResult!;
248  
      ret (empty(scorer.errors)
249  
        ? p("Pattern analysis: No problems found. " + nEntries(countConcepts(QA)) + ".")
250  
        : joinMap(scorer.errors, lambda1 renderErrorAsParagraph))
251  
        + super.renderTable(withCmds);
252  
    }
253  
    
254  
    S renderErrorAsParagraph(ConsistencyError error) {
255  
      S fix = error.renderFix();
256  
      ret p("Error: " + htmlEncode2(error.msg) + " " +
257  
        joinMap(error.items, qa -> ahref(editLink(qa.id), "[item]"))
258  
        + (empty(fix) ? "" : " " + fix));
259  
    }
260  
  };
261  
  
262  
  // actions
263  
264  
  if (eqGet(params, action := 'reindex)) {
265  
    long id = parseLong(params.get('id));
266  
    int newIndex = parseInt(params.get('newIndex));
267  
    QA qa = getConcept(QA, id);
268  
    if (qa == null) ret crud.refreshWithMsgs("Item not found");
269  
    cset(qa, index := newIndex);
270  
    reindexQAs();
271  
    ret crud.refreshWithMsgs("Index of item " + qa.id + " changed");
272  
  }
273  
274  
  if (eqGet(params, botOn := "1")) {
275  
    cset(uniq(Settings), botOn := true);
276  
    ret crud.refreshWithMsgs("Bot turned on!");
277  
  }
278  
  
279  
  if (eqGet(params, botOff := "1")) {
280  
    cset(uniq(Settings), botOn := false);
281  
    ret crud.refreshWithMsgs("Bot turned off!");
282  
  }
283  
  
284  
  if (nemptyGet botAutoOpen(params)) {
285  
    cset(uniq(Settings), botAutoOpen := eq("1", params.get("botAutoOpen")));
286  
    ret crud.refreshWithMsgs("Bot auto-open turned " + onOff(botAutoOpen()) + "!");
287  
  }
288  
  
289  
  ret crud.renderPage(params);
290  
 } catch print e {
291  
  ret "ERROR.";
292  
 }
293  
}
294  
295  
static new ThreadLocal<S> language_out;
296  
static new ThreadLocal<Bool> opt_noDefault; // true = return null instead of #default text
297  
298  
static new ThreadLocal<Cl<S>> categoriesForRequest;
299  
300  
// main API function for other bots
301  
sS answer(S s, S language) {
302  
  language_out.set(null);
303  
  ret trim(answer_main(s, language));
304  
}
305  
306  
sS answer_main(S s, S language) {
307  
  S raw = findRawAnswer(s, language, true);
308  
  S contents = extractKeywordPlusBracketed_keepComments("random", raw);
309  
  if (contents != null)
310  
    ret random(splitAtEmptyLines(contents));
311  
  ret raw;
312  
}
313  
314  
sS findRawAnswer(S s, S language, bool allowTypos) {
315  
  ret selectQA(findMatchingQA(s, language, allowTypos));
316  
}
317  
318  
static QA findQAWithTypos(S s, S language) {
319  
  Lowest<QA> qa = findClosestQA(s, language);
320  
  if (!qa.has()) null;
321  
  if (qa.score() > maxTypos) {
322  
    print("Rejecting " + nTypos((int) qa.score()) + ": " + s + " / " + qa->patterns);
323  
    null;
324  
  }
325  
  print("Accepting " + nTypos((int) qa.score()) + ": " + s + " / " + qa->patterns);
326  
  ret qa!;
327  
}
328  
329  
static Cl<QA> allQAsForRequest(S language) {
330  
  Cl<QA> l = conceptsWhere(QA, +language);
331  
  if (categoriesForRequest! != null)
332  
    l = filter(l, qa -> contains(categoriesForRequest!, qa.category));
333  
  ret l;
334  
}
335  
336  
static QA findMatchingQA(S s, S language, bool allowTypos) {
337  
  try object QA qa = findMatchingQA(s, allQAsForRequest(language));
338  
  try object QA qa = findMatchingQA(s, filter(list(QA), q -> neq(q.language, language))); // TODO: categoriesForRequest
339  
  if (allowTypos)
340  
    try object QA qa = findQAWithTypos(s, language);
341  
  if (!eq(s, "#default") && !isTrue(opt_noDefault!)) ret findMatchingQA("#default", language, false);
342  
  null;
343  
}
344  
345  
static Lowest<QA> findClosestQA(S s, S language) {
346  
  time "findClosestQA" {
347  
    new Lowest<QA> qa;
348  
    findClosestQA(s, qa, allQAsForRequest(language));
349  
    findClosestQA(s, qa, filter(list(QA), q -> neq(q.language, language)));
350  
  }
351  
  ret qa;
352  
}
353  
354  
static L<QA> sortQAs(Cl<QA> qas) {
355  
  Map<S, Int> categoryToIndex = fieldToFieldIndex('name, 'index, list(Category));
356  
  ret sortedByCalculatedField(qas, q -> pair(categoryToIndex.get(q.category), q.index));
357  
}
358  
359  
svoid reindexQAs() {
360  
  for (Language lang) {
361  
    int index = 1;
362  
    for (QA qa : sortQAs(conceptsWhere(QA, language := lang.code)))
363  
      cset(qa, index := index++);
364  
  }
365  
}
366  
367  
static QA findMatchingQA(S s, Cl<QA> qas) {
368  
  for (QA qa : sortQAs(qas))
369  
    if (mmo_match_parsedPattern(qa.parsedPattern(), s))
370  
      ret qa;
371  
  null;
372  
}
373  
374  
svoid findClosestQA(S s, Lowest<QA> best, Cl<QA> qas) {
375  
  for (QA qa : sortQAs(qas)) {
376  
    Int score = mmo_levenWithSwapsScore_parsedPattern(qa.parsedPattern(), s);
377  
    if (score != null)
378  
      best.put(qa, score);
379  
  }
380  
}
381  
382  
// returns answers
383  
sS selectQA(QA qa) {
384  
  if (qa == null) null;
385  
  language_out.set(qa.language);
386  
  ret qa.answers;
387  
}
388  
389  
sclass ConsistencyError {
390  
  S msg;
391  
  L<QA> items;
392  
  
393  
  *(S *msg, QA... items) { this.items = asList(items); }
394  
  
395  
  S renderFix() { null; }
396  
}
397  
398  
svoid checkLocalConsistency(QA qa, Scorer<ConsistencyError> scorer) {
399  
  LS questions = tlft(qa.questions);
400  
  for (S q : questions)
401  
    if (mmo_match_parsedPattern(qa.parsedPattern(), q))
402  
      scorer.ok();
403  
    else
404  
      scorer.error(ConsistencyError("Question " + quote(q) + " not matched by patterns " + quote(qa.patterns), qa));
405  
}
406  
407  
svoid checkGlobalConsistency(QA qa, Scorer<ConsistencyError> scorer) {
408  
  for (S q : tlft(qa.questions)) {
409  
    QA found = findMatchingQA(q, qa.language, false);
410  
    if (found != null && found != qa)
411  
      scorer.error(new ConsistencyError("Question " + quote(q) + " (item " + qa.id + ") shadowed by patterns " + quote(found.patterns) + " (item " + found.id + ")", qa, found) {
412  
        S renderFix() {
413  
          ret ahref(rawLink("?action=reindex&id=" + qa.id + "&newIndex=" + (found.index-1)), "[fix by changing index]");
414  
        }
415  
      });
416  
    else
417  
      scorer.ok();
418  
  }
419  
}
420  
421  
static Scorer<ConsistencyError> consistencyCheck() {
422  
  new Scorer<ConsistencyError> scorer;
423  
  scorer.collectErrors();
424  
  for (QA qa) {
425  
    checkLocalConsistency(qa, scorer);
426  
    checkGlobalConsistency(qa, scorer);
427  
  }
428  
  ret scorer;
429  
}
430  
431  
sS contactFormViberID() {
432  
  ret uniq(Settings).contactFormViberID;
433  
}
434  
435  
// API
436  
437  
sbool botOn() {
438  
  ret uniq(Settings).botOn;
439  
}
440  
441  
sbool botAutoOpen() {
442  
  ret uniq(Settings).botAutoOpen;
443  
}
444  
445  
svoid importQA(virtual QA qa_external) {
446  
  QA qa = shallowCloneToUnlistedConcept QA(qa_external);
447  
  uniq QA(allConceptFieldsAsParams(qa));
448  
}
449  
450  
sS rawLink() { ret "/"; }
451  
sS rawLink(S uri) { ret addSlashPrefix(uri) ; }

Author comment

Began life as a copy of #1028036

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: #1028043
Snippet name: Tomii Boi Answers DB Module backup 1
Eternal ID of this version: #1028043/1
Text MD5: 3657674ade2740a4949191d36ba315a3
Transpilation MD5: 83ca3d369f461164cf8444030974b9c5
Author: stefan
Category: javax / html
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-05-07 19:51:26
Source code size: 13126 bytes / 451 lines
Pitched / IR pitched: No / No
Views / Downloads: 160 / 219
Referenced in: [show references]