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: | 455 / 1772 |
| Version history: | 85 change(s) |
| Referenced in: | [show references] |