Uses 911K of libraries. Click here for Pure Java version (17189L/92K).
1 | !7 |
2 | |
3 | concept Cmd { |
4 | S patterns; // for mmo_parsePattern |
5 | S exampleInputs; // line-separated |
6 | S cmd; // a star pattern recognized by the backend |
7 | S questionsForArguments; // line-separated |
8 | S conditions; |
9 | int index; |
10 | bool autoGenerated; |
11 | |
12 | LS translatableFields() { ret splitAtSpace("patterns exampleInputs cmd questionsForArguments"); } |
13 | |
14 | transient MMOPattern parsedPattern; |
15 | void change { parsedPattern = null; super.change(); } |
16 | |
17 | MMOPattern parsedPattern() { |
18 | if (parsedPattern == null) parsedPattern = mmo_parsePattern(patterns); |
19 | ret parsedPattern; |
20 | } |
21 | |
22 | sS _fieldOrder = "autoGenerated exampleInputs patterns cmd questionsForArguments conditions"; |
23 | } |
24 | |
25 | concept Replacement { |
26 | S in, out; // word to replace and which word to replace it with |
27 | S except; // IDs of Cmd concepts to skip |
28 | |
29 | sS _fieldOrder = "in out except"; |
30 | } |
31 | |
32 | concept Mishearing { |
33 | S in, out; |
34 | } |
35 | |
36 | cmodule ChatBotFrontend > DynCRUD<Cmd> { |
37 | switchable S backendModuleLibID = "#1027443/Scenarios"; |
38 | O currentAttractor; |
39 | transient CRUD<Replacement> replacementsCRUD = new(Replacement); |
40 | transient CRUD<Mishearing> mishearingsCRUD = new(Mishearing); |
41 | |
42 | class HandleArgument implements IF1<S> { |
43 | S cmd; |
44 | LS argQuestions; |
45 | new LS arguments; |
46 | |
47 | *() {} |
48 | *(S *cmd, LS *argQuestions) {} |
49 | |
50 | // process an argument |
51 | public S get(S arg) { |
52 | arguments.add(arg); |
53 | if (l(arguments) >= countAsteriskTokens(cmd)) |
54 | ret sendToBackend(format_quoteAll(cmd, asObjectArray(arguments))); |
55 | else |
56 | ret handleQuestionWithArguments(this); |
57 | } |
58 | |
59 | S currentQuestion() { |
60 | ret or2(_get(argQuestions, l(arguments)), "Need argument"); |
61 | } |
62 | } |
63 | |
64 | runnable class Cancel { setField(currentAttractor := null); } |
65 | |
66 | class UndoHandler implements IF1<Bool, S> { |
67 | public S get(Bool yes) { |
68 | if (!yes) ret "OK"; // user said no |
69 | ret (S) sendToBackend("undo"); |
70 | } |
71 | } |
72 | |
73 | start { |
74 | dm_watchFieldAndNow backendModuleLibID(r { |
75 | dm_setModuleName("Frontend for " + dm_moduleName(backend())) |
76 | }); |
77 | |
78 | crud.multiLineFields = litset("exampleInputs", "questionsForArguments"); |
79 | crud.sorter = func(Cl<Cmd> l) -> L<Cmd> { sortByField index(l) }; |
80 | crud.formFixer = 48; |
81 | } |
82 | |
83 | S handleCommand(Cmd cmd) { |
84 | if (cmd == null) null; |
85 | |
86 | print("handleCommand " + cmd.cmd); |
87 | |
88 | if (match("undo after confirm", cmd.cmd)) { |
89 | O undo = dm_call(backend(), 'lastUndo); |
90 | if (undo == null) ret "Nothing to undo"; |
91 | new WaitForAnswer_YesNo attractor; |
92 | attractor.question = "Undo " + undo + "?"; |
93 | setField(currentAttractor := attractor); |
94 | print("Set attractor to: " + attractor); |
95 | attractor.processAnswer = new UndoHandler; |
96 | attractor.cancelSilently = new Cancel; |
97 | ret attractor.question; |
98 | } |
99 | |
100 | if (hasAsteriskTokens(cmd.cmd)) |
101 | ret handleQuestionWithArguments( |
102 | new HandleArgument(cmd.cmd, tlft(cmd.questionsForArguments))); |
103 | else |
104 | ret sendToBackend(cmd.cmd); |
105 | } |
106 | |
107 | S handleQuestionWithArguments(HandleArgument handler) { |
108 | new WaitForName attractor; |
109 | attractor.question = handler.currentQuestion(); |
110 | setField(currentAttractor := attractor); |
111 | print("Set attractor to: " + attractor); |
112 | attractor.processName = handler; |
113 | attractor.cancelSilently = new Cancel; |
114 | ret attractor.question; |
115 | } |
116 | |
117 | S sendToBackend(S cmd) { |
118 | ret (S) dm_call(backend(), 'answer, cmd); |
119 | } |
120 | |
121 | S backend() { |
122 | ret dm_require(backendModuleLibID); |
123 | } |
124 | |
125 | visual { |
126 | JComponent mainCRUD = super.visualize(); |
127 | |
128 | addComponents(crud.buttons, |
129 | jbutton("Talk to me", rThread talkToMe), |
130 | jPopDownButton_noText( |
131 | "Apply replacements", rThread applyReplacements, |
132 | "Consistency check", rThread consistencyCheck)); |
133 | |
134 | ret jtabs( |
135 | "Commands" := mainCRUD, |
136 | "Replacements" := replacementsCRUD.visualize(), |
137 | "Mishearings" := mishearingsCRUD.visualize()); |
138 | } |
139 | |
140 | void talkToMe enter { dm_showConversationPopupForModule(); } |
141 | |
142 | void consistencyCheck enter { |
143 | reindex(); |
144 | L<Cmd> cmds = list(Cmd); |
145 | L<MMOConsistencyError> errors = mmo_consistencyCheck( |
146 | map(cmds, cmd -> pair(cmd.exampleInputs, cmd.patterns))); |
147 | if (empty(errors)) infoBox("No consistency problems"); |
148 | else |
149 | dm_showText(n2(errors, "consistency problem"), lines(errors)); |
150 | } |
151 | |
152 | void reindex { |
153 | int index = 1; |
154 | for (Cmd cmd : conceptsSortedByFields Cmd('autoGenerated, 'index)) |
155 | cset(cmd, index := index++); |
156 | } |
157 | |
158 | void applyReplacements { |
159 | deleteConceptsWhere Cmd(autoGenerated := true); |
160 | for (Cmd cmd) |
161 | for (Replacement r) { |
162 | if (jcontains(r.except, str(cmd.id))) continue; |
163 | SS map = litcimap(r.in, r.out, plural(r.in), plural(r.out)); |
164 | Cmd cmd2 = shallowCloneUnlisted(cmd); |
165 | cset(cmd2, autoGenerated := true); |
166 | for (S field : cmd.translatableFields()) |
167 | cset(cmd2, field, fixAOrAn(replacePhrases(map, getString(cmd, field)))); |
168 | if (anyFieldsDifferent(cmd, cmd2, cmd.translatableFields())) |
169 | registerConcept(cmd2); |
170 | } |
171 | consistencyCheck(); |
172 | } |
173 | |
174 | // API |
175 | |
176 | // for initial fill of translation table. probably doesn't catch many patterns usually |
177 | void copyCommandsFromBackend() { |
178 | for (S cmd : allIfQuotedPatterns(loadSnippet(beforeSlash(backendModuleLibID)))) |
179 | uniqConcept(+cmd); |
180 | } |
181 | |
182 | S answer(S s) { |
183 | S s1 = s; |
184 | s = replacePhrases(fieldToFieldIndexCI('in, 'out, list(Mishearing)), s); |
185 | if (neq(s, s1)) print("Corrected to: " + s); |
186 | if (currentAttractor != null) { |
187 | print("Sending to attractor " + currentAttractor + ": " + s); |
188 | S a = strOrNull(call(currentAttractor, 'answer, s)); |
189 | if (a != null) { |
190 | print("Attractor said: " + a); |
191 | ret a; |
192 | } |
193 | } |
194 | L<Cmd> cmds = filter(conceptsSortedByField Cmd('index), |
195 | c -> nempty(c.patterns) && checkCondition(c)); |
196 | Cmd cmd = mmo_matchMultiWithTypos(1, cmds, c -> c.parsedPattern(), s); |
197 | if (cmd != null) ret handleCommand(cmd); |
198 | ret sendToBackend(s); |
199 | } |
200 | |
201 | bool checkCondition(Cmd cmd) { |
202 | if (match("have scenario", cmd.conditions)) |
203 | ret print("have scenario", dm_call(backend(), 'selectedScenario)) != null; |
204 | true; |
205 | } |
206 | } |
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: | #1027518 |
Snippet name: | Chat Bot Frontend [with command translations, e.g. for Scenarios] |
Eternal ID of this version: | #1027518/86 |
Text MD5: | 8ee6614dcc555cedfe6a2fed609a73bf |
Transpilation MD5: | ef037be1a965005ac41b40bdd80ea89f |
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-24 11:29:17 |
Source code size: | 6365 bytes / 206 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 253 / 1528 |
Version history: | 85 change(s) |
Referenced in: | [show references] |