Warning: session_start(): open(/var/lib/php/sessions/sess_36faevris26af19i823rmvh29v, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
!include once #1027578 // Named
// Note: db_mainConcepts() doesn't work (because of cases), always use cc!
concept BotName > Named {} // e.g. for finding the module in Cruddie
concept Cmd > ConceptWithGlobalID {
S patterns; // for mmo_parsePattern
S exampleInputs; // line-separated
S cmd; // a star pattern recognized by the backend
S questionsForArguments; // line-separated
S conditions;
int index;
bool autoGenerated;
LS translatableFields() { ret splitAtSpace("patterns exampleInputs cmd questionsForArguments"); }
sS importableFields() { ret "cmd conditions exampleInputs globalID index patterns questionsForArguments"; }
transient MMOPattern parsedPattern;
void change { parsedPattern = null; super.change(); }
MMOPattern parsedPattern() {
if (parsedPattern == null) parsedPattern = mmo_parsePattern(patterns);
ret parsedPattern;
}
sS _fieldOrder = "autoGenerated exampleInputs patterns cmd questionsForArguments conditions";
}
concept Replacement {
S in, out; // word to replace and which word to replace it with
S except; // IDs of Cmd concepts to skip
sS _fieldOrder = "in out except";
}
concept Mishearing {
S in, out;
}
cmodule ChatBotFrontend > DynCRUD {
switchable S backendModuleID;
switchable S baseThingName = "$thing";
O currentAttractor;
transient CRUD replacementsCRUD, mishearingsCRUD;
class HandleArgument implements IF1 {
S cmd;
LS argQuestions;
new LS arguments;
*() {}
*(S *cmd, LS *argQuestions) {}
// process an argument
public S get(S arg) {
arguments.add(arg);
if (l(arguments) >= countAsteriskTokens(cmd))
ret sendToBackend(format_quoteAll(cmd, asObjectArray(arguments)));
else
ret handleQuestionWithArguments(this);
}
S currentQuestion() {
ret or2(_get(argQuestions, l(arguments)), "Need argument");
}
}
runnable class Cancel { setField(currentAttractor := null); }
class UndoHandler implements IF1 {
public S get(Bool yes) {
if (!yes) ret "OK"; // user said no
ret (S) sendToBackend("undo");
}
}
void start {
cc = dm_handleCaseIDField();
super.start();
dm_watchFieldAndNow backendModuleID(r updateModuleName);
crud.multiLineFields = litset("exampleInputs", "questionsForArguments");
crud.sorter = func(Cl l) -> L { sortByField index(l) };
crud.formFixer = 48;
replacementsCRUD = new CRUD(cc, Replacement);
mishearingsCRUD = new CRUD(cc, Mishearing);
dm_vmBus_onMessage_q objectTypesChanged((module, names) -> {
if (dm_isSame(module, backend()))
importReplacements();
});
}
void importReplacements() {
LS names = cast dm_call(backend(), 'objectTypes);
print("Got names: " + names);
if (names != null) {
deleteConcepts(cc, Replacement);
for (S name : names)
if (neqic(name, baseThingName))
cnew(cc, Replacement, in := baseThingName, out := name);
}
applyReplacements();
}
S handleCommand(Cmd cmd) {
if (cmd == null) null;
print("handleCommand " + cmd.cmd);
if (match("undo after confirm", cmd.cmd)) {
O undo = dm_call(backend(), 'lastUndo);
if (undo == null) ret "Nothing to undo";
new WaitForAnswer_YesNo attractor;
attractor.question = "Undo " + undo + "?";
setField(currentAttractor := attractor);
print("Set attractor to: " + attractor);
attractor.processAnswer = new UndoHandler;
attractor.cancelSilently = new Cancel;
ret attractor.question;
}
if (hasAsteriskTokens(cmd.cmd))
ret handleQuestionWithArguments(
new HandleArgument(cmd.cmd, tlft(cmd.questionsForArguments)));
else
ret sendToBackend(cmd.cmd);
}
S handleQuestionWithArguments(HandleArgument handler) {
new WaitForName attractor;
attractor.question = handler.currentQuestion();
setField(currentAttractor := attractor);
print("Set attractor to: " + attractor);
attractor.processName = handler;
attractor.cancelSilently = new Cancel;
ret attractor.question;
}
S sendToBackend(S cmd) {
ret (S) dm_call(backend(), 'answer, cmd);
}
S backend() {
ret backendModuleID;
}
visual {
JComponent mainCRUD = super.visualize();
addComponents(crud.buttons,
jbutton("Talk to me", rThread talkToMe),
jPopDownButton_noText(
"Apply replacements", rThread applyReplacements,
"Import replacements", rThread importReplacements,
"Consistency check", rThread consistencyCheck,
"Import commands from snippet...", rThread importCmdsFromSnippet));
ret jtabs(
"Commands" := mainCRUD,
"Replacements" := replacementsCRUD.visualize(),
"Mishearings" := mishearingsCRUD.visualize());
}
void talkToMe enter { dm_showConversationPopupForModule(); }
void consistencyCheck enter {
reindex();
L cmds = list(Cmd);
L errors = mmo_consistencyCheck(
map(cmds, cmd -> pair(cmd.exampleInputs, cmd.patterns)));
if (empty(errors)) infoBox("No consistency problems");
else
dm_showText(n2(errors, "consistency problem"), lines(errors));
}
void reindex {
int index = 1;
for (Cmd cmd : conceptsSortedByFields(cc, Cmd, 'autoGenerated, 'index))
cset(cmd, index := index++);
}
void applyReplacements {
deleteConceptsWhere(cc, Cmd, autoGenerated := true);
for (Cmd cmd)
for (Replacement r) {
if (jcontains(r.except, str(cmd.id))) continue;
SS map = litcimap(r.in, r.out, plural(r.in), plural(r.out));
Cmd cmd2 = shallowCloneUnlisted(cmd);
cset(cmd2, autoGenerated := true, globalID := aGlobalIDObject());
for (S field : cmd.translatableFields())
cset(cmd2, field, fixAOrAn(replacePhrases(map, getString(cmd, field))));
if (anyFieldsDifferent(cmd, cmd2, cmd.translatableFields()))
registerConcept(cc, cmd2);
}
consistencyCheck();
}
// API
// for initial fill of translation table. probably doesn't catch many patterns usually
void copyCommandsFromBackend() {
for (S cmd : allIfQuotedPatterns(loadSnippet(beforeSlash(dm_moduleLibID(backend())))))
uniqConcept(+cmd);
}
S answer(S s) {
S s1 = s;
s = replacePhrases(fieldToFieldIndexCI('in, 'out, list(Mishearing)), s);
if (neq(s, s1)) print("Corrected to: " + s);
if (currentAttractor != null) {
print("Sending to attractor " + currentAttractor + ": " + s);
S a = strOrNull(call(currentAttractor, 'answer, s));
if (a != null) {
print("Attractor said: " + a);
ret a;
}
}
L cmds = filter(conceptsSortedByField(cc, Cmd, 'index),
c -> nempty(c.patterns) && checkCondition(c));
Cmd cmd = mmo_matchMultiWithTypos(1, cmds, c -> c.parsedPattern(), s);
if (cmd != null) ret handleCommand(cmd);
ret sendToBackend(s);
}
bool checkCondition(Cmd cmd) {
true;
}
// e.g. from snippet #1027616
void importCmds(S src) {
L l = dynShortNamed Cmd(safeUnstructList(src));
int imported = 0;
for (O o : l) {
S id = getString globalID(o);
if (empty(id)) continue;
++imported;
GlobalID globalID = GlobalID(id);
Cmd cmd = uniq_sync(cc, Cmd, +globalID);
for (S field : splitAtSpace(Cmd.importableFields()))
cSmartSet(cmd, field, getOpt(o, field));
}
topLeftInfoBox("Imported/updated " + nEntries(imported));
if (imported != 0)
importReplacements();
}
void importCmdsFromSnippet(S snippetID) {
print("Importing cmds from " + snippetID);
importCmds(loadSnippet(snippetID));
}
// import if we have no cmds yet
void importCmdsFromSnippetIfEmpty(S snippetID) {
if (conceptCount() == 0)
importCmdsFromSnippet(snippetID);
}
void importCmdsFromSnippet enter {
selectSnippetID("Commands to import", vf importCmdsFromSnippet);
}
void connectToBackend(S backendModuleID) {
setField(+backendModuleID);
}
void setBotName(S name) {
cset(uniq(cc, BotName), +name);
updateModuleName();
}
// may return null or empty string
S botName() {
ret getString name(conceptWhere(cc, BotName));
}
void updateModuleName enter {
S name = botName();
if (empty(name)) name = dm_moduleName(backend());
dm_setModuleName("Frontend for " + name);
}
}