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: | 406 / 515 |
| Referenced in: | [show references] |