Uses 911K of libraries. Click here for Pure Java version (20571L/112K).
1 | !7 |
2 | |
3 | !include once #1027578 // Named |
4 | |
5 | // Note: db_mainConcepts() doesn't work (because of cases), always use cc! |
6 | |
7 | concept BotName > Named {} // e.g. for finding the module in Cruddie |
8 | |
9 | concept Cmd > ConceptWithGlobalID { |
10 | S patterns; // for mmo_parsePattern |
11 | S exampleInputs; // line-separated |
12 | S cmd; // a star pattern recognized by the backend |
13 | S questionsForArguments; // line-separated |
14 | S conditions; |
15 | int index; |
16 | bool autoGenerated; |
17 | |
18 | LS translatableFields() { ret splitAtSpace("patterns exampleInputs cmd questionsForArguments"); } |
19 | |
20 | sS importableFields() { ret "cmd conditions exampleInputs globalID index patterns questionsForArguments"; } |
21 | |
22 | transient MMOPattern parsedPattern; |
23 | void change { parsedPattern = null; super.change(); } |
24 | |
25 | MMOPattern parsedPattern() { |
26 | if (parsedPattern == null) parsedPattern = mmo_parsePattern(patterns); |
27 | ret parsedPattern; |
28 | } |
29 | |
30 | sS _fieldOrder = "autoGenerated exampleInputs patterns cmd questionsForArguments conditions"; |
31 | } |
32 | |
33 | concept Replacement { |
34 | S in, out; // word to replace and which word to replace it with |
35 | S except; // IDs of Cmd concepts to skip |
36 | |
37 | sS _fieldOrder = "in out except"; |
38 | } |
39 | |
40 | concept Mishearing { |
41 | S in, out; |
42 | } |
43 | |
44 | cmodule ChatBotFrontend > DynCRUD<Cmd> { |
45 | switchable S backendModuleID; |
46 | switchable S baseThingName = "$thing"; |
47 | O currentAttractor; |
48 | transient CRUD<Replacement> replacementsCRUD, mishearingsCRUD; |
49 | |
50 | class HandleArgument implements IF1<S> { |
51 | S cmd; |
52 | LS argQuestions; |
53 | new LS arguments; |
54 | |
55 | *() {} |
56 | *(S *cmd, LS *argQuestions) {} |
57 | |
58 | // process an argument |
59 | public S get(S arg) { |
60 | arguments.add(arg); |
61 | if (l(arguments) >= countAsteriskTokens(cmd)) |
62 | ret sendToBackend(format_quoteAll(cmd, asObjectArray(arguments))); |
63 | else |
64 | ret handleQuestionWithArguments(this); |
65 | } |
66 | |
67 | S currentQuestion() { |
68 | ret or2(_get(argQuestions, l(arguments)), "Need argument"); |
69 | } |
70 | } |
71 | |
72 | runnable class Cancel { setField(currentAttractor := null); } |
73 | |
74 | class UndoHandler implements IF1<Bool, S> { |
75 | public S get(Bool yes) { |
76 | if (!yes) ret "OK"; // user said no |
77 | ret (S) sendToBackend("undo"); |
78 | } |
79 | } |
80 | |
81 | void start { |
82 | cc = dm_handleCaseIDField(); |
83 | super.start(); |
84 | dm_watchFieldAndNow backendModuleID(r updateModuleName); |
85 | |
86 | crud.multiLineFields = litset("exampleInputs", "questionsForArguments"); |
87 | crud.sorter = func(Cl<Cmd> l) -> L<Cmd> { sortByField index(l) }; |
88 | crud.formFixer = 48; |
89 | |
90 | replacementsCRUD = new CRUD(cc, Replacement); |
91 | mishearingsCRUD = new CRUD(cc, Mishearing); |
92 | |
93 | dm_vmBus_onMessage_q objectTypesChanged((module, names) -> { |
94 | if (dm_isSame(module, backend())) |
95 | importReplacements(); |
96 | }); |
97 | } |
98 | |
99 | void importReplacements() { |
100 | LS names = cast dm_call(backend(), 'objectTypes); |
101 | print("Got names: " + names); |
102 | if (names != null) { |
103 | deleteConcepts(cc, Replacement); |
104 | for (S name : names) |
105 | if (neqic(name, baseThingName)) |
106 | cnew(cc, Replacement, in := baseThingName, out := name); |
107 | } |
108 | applyReplacements(); |
109 | } |
110 | |
111 | S handleCommand(Cmd cmd) { |
112 | if (cmd == null) null; |
113 | |
114 | print("handleCommand " + cmd.cmd); |
115 | |
116 | if (match("undo after confirm", cmd.cmd)) { |
117 | O undo = dm_call(backend(), 'lastUndo); |
118 | if (undo == null) ret "Nothing to undo"; |
119 | new WaitForAnswer_YesNo attractor; |
120 | attractor.question = "Undo " + undo + "?"; |
121 | setField(currentAttractor := attractor); |
122 | print("Set attractor to: " + attractor); |
123 | attractor.processAnswer = new UndoHandler; |
124 | attractor.cancelSilently = new Cancel; |
125 | ret attractor.question; |
126 | } |
127 | |
128 | if (hasAsteriskTokens(cmd.cmd)) |
129 | ret handleQuestionWithArguments( |
130 | new HandleArgument(cmd.cmd, tlft(cmd.questionsForArguments))); |
131 | else |
132 | ret sendToBackend(cmd.cmd); |
133 | } |
134 | |
135 | S handleQuestionWithArguments(HandleArgument handler) { |
136 | new WaitForName attractor; |
137 | attractor.question = handler.currentQuestion(); |
138 | setField(currentAttractor := attractor); |
139 | print("Set attractor to: " + attractor); |
140 | attractor.processName = handler; |
141 | attractor.cancelSilently = new Cancel; |
142 | ret attractor.question; |
143 | } |
144 | |
145 | S sendToBackend(S cmd) { |
146 | ret (S) dm_call(backend(), 'answer, cmd); |
147 | } |
148 | |
149 | S backend() { |
150 | ret backendModuleID; |
151 | } |
152 | |
153 | visual { |
154 | JComponent mainCRUD = super.visualize(); |
155 | |
156 | addComponents(crud.buttons, |
157 | jbutton("Talk to me", rThread talkToMe), |
158 | jPopDownButton_noText( |
159 | "Apply replacements", rThread applyReplacements, |
160 | "Import replacements", rThread importReplacements, |
161 | "Consistency check", rThread consistencyCheck, |
162 | "Import commands from snippet...", rThread importCmdsFromSnippet)); |
163 | |
164 | ret jtabs( |
165 | "Commands" := mainCRUD, |
166 | "Replacements" := replacementsCRUD.visualize(), |
167 | "Mishearings" := mishearingsCRUD.visualize()); |
168 | } |
169 | |
170 | void talkToMe enter { dm_showConversationPopupForModule(); } |
171 | |
172 | void consistencyCheck enter { |
173 | reindex(); |
174 | L<Cmd> cmds = list(Cmd); |
175 | L<MMOConsistencyError> errors = mmo_consistencyCheck( |
176 | map(cmds, cmd -> pair(cmd.exampleInputs, cmd.patterns))); |
177 | if (empty(errors)) infoBox("No consistency problems"); |
178 | else |
179 | dm_showText(n2(errors, "consistency problem"), lines(errors)); |
180 | } |
181 | |
182 | void reindex { |
183 | int index = 1; |
184 | for (Cmd cmd : conceptsSortedByFields(cc, Cmd, 'autoGenerated, 'index)) |
185 | cset(cmd, index := index++); |
186 | } |
187 | |
188 | void applyReplacements { |
189 | deleteConceptsWhere(cc, Cmd, autoGenerated := true); |
190 | for (Cmd cmd) |
191 | for (Replacement r) { |
192 | if (jcontains(r.except, str(cmd.id))) continue; |
193 | SS map = litcimap(r.in, r.out, plural(r.in), plural(r.out)); |
194 | Cmd cmd2 = shallowCloneUnlisted(cmd); |
195 | cset(cmd2, autoGenerated := true, globalID := aGlobalIDObject()); |
196 | for (S field : cmd.translatableFields()) |
197 | cset(cmd2, field, fixAOrAn(replacePhrases(map, getString(cmd, field)))); |
198 | if (anyFieldsDifferent(cmd, cmd2, cmd.translatableFields())) |
199 | registerConcept(cc, cmd2); |
200 | } |
201 | consistencyCheck(); |
202 | } |
203 | |
204 | // API |
205 | |
206 | // for initial fill of translation table. probably doesn't catch many patterns usually |
207 | void copyCommandsFromBackend() { |
208 | for (S cmd : allIfQuotedPatterns(loadSnippet(beforeSlash(dm_moduleLibID(backend()))))) |
209 | uniqConcept(+cmd); |
210 | } |
211 | |
212 | S answer(S s) { |
213 | S s1 = s; |
214 | s = replacePhrases(fieldToFieldIndexCI('in, 'out, list(Mishearing)), s); |
215 | if (neq(s, s1)) print("Corrected to: " + s); |
216 | if (currentAttractor != null) { |
217 | print("Sending to attractor " + currentAttractor + ": " + s); |
218 | S a = strOrNull(call(currentAttractor, 'answer, s)); |
219 | if (a != null) { |
220 | print("Attractor said: " + a); |
221 | ret a; |
222 | } |
223 | } |
224 | L<Cmd> cmds = filter(conceptsSortedByField(cc, Cmd, 'index), |
225 | c -> nempty(c.patterns) && checkCondition(c)); |
226 | Cmd cmd = mmo_matchMultiWithTypos(1, cmds, c -> c.parsedPattern(), s); |
227 | if (cmd != null) ret handleCommand(cmd); |
228 | ret sendToBackend(s); |
229 | } |
230 | |
231 | bool checkCondition(Cmd cmd) { |
232 | true; |
233 | } |
234 | |
235 | // e.g. from snippet #1027616 |
236 | void importCmds(S src) { |
237 | L l = dynShortNamed Cmd(safeUnstructList(src)); |
238 | int imported = 0; |
239 | for (O o : l) { |
240 | S id = getString globalID(o); |
241 | if (empty(id)) continue; |
242 | ++imported; |
243 | GlobalID globalID = GlobalID(id); |
244 | Cmd cmd = uniq_sync(cc, Cmd, +globalID); |
245 | for (S field : splitAtSpace(Cmd.importableFields())) |
246 | cSmartSet(cmd, field, getOpt(o, field)); |
247 | } |
248 | topLeftInfoBox("Imported/updated " + nEntries(imported)); |
249 | if (imported != 0) |
250 | importReplacements(); |
251 | } |
252 | |
253 | void importCmdsFromSnippet(S snippetID) { |
254 | print("Importing cmds from " + snippetID); |
255 | importCmds(loadSnippet(snippetID)); |
256 | } |
257 | |
258 | // import if we have no cmds yet |
259 | void importCmdsFromSnippetIfEmpty(S snippetID) { |
260 | if (conceptCount() == 0) |
261 | importCmdsFromSnippet(snippetID); |
262 | } |
263 | |
264 | void importCmdsFromSnippet enter { |
265 | selectSnippetID("Commands to import", vf<S> importCmdsFromSnippet); |
266 | } |
267 | |
268 | void connectToBackend(S backendModuleID) { |
269 | setField(+backendModuleID); |
270 | } |
271 | |
272 | void setBotName(S name) { |
273 | cset(uniq(cc, BotName), +name); |
274 | updateModuleName(); |
275 | } |
276 | |
277 | // may return null or empty string |
278 | S botName() { |
279 | ret getString name(conceptWhere(cc, BotName)); |
280 | } |
281 | |
282 | void updateModuleName enter { |
283 | S name = botName(); |
284 | if (empty(name)) name = dm_moduleName(backend()); |
285 | dm_setModuleName("Frontend for " + name); |
286 | } |
287 | } |
Began life as a copy of #1027602
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: | #1027661 |
Snippet name: | Chat Bot Frontend v2 backup |
Eternal ID of this version: | #1027661/1 |
Text MD5: | daa04656d3e46badf660de40898e4945 |
Transpilation MD5: | ed4641f26d58b57ac018eb6b19fa15c5 |
Author: | stefan |
Category: | javax |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-03-29 21:27:20 |
Source code size: | 8784 bytes / 287 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 175 / 238 |
Referenced in: | [show references] |