!7 sS dbBotID = #1026247; static Env env; static new ThreadLocal session; static new ThreadLocal out; sclass Out { S buttonsIntro; LS buttons; S placeholder; S defaultInput; } sbool testFunctions = false; static interface Env { void sayAsync(S session, S msg); } concept Session { S cookie; S language; S lastQuestion; FormInFlight form, lastForm; void cancelForm { cset(this, lastForm := form, form := null); } S language() { ret or2(language, 'en); } } sclass FormStep { S key; S displayText, desc; S defaultValue; S placeholder; // null for same as displayText, "" for none LS buttons; S value; void update(Runnable onChange) {} } sclass FormInFlight { S hashTag; new L steps; int stepIndex; // in steps list FormStep currentStep() { ret get(steps, stepIndex); } void update(Runnable onChange) { if (currentStep() != null) currentStep().update(onChange); } S complete() { ret "Form complete"; } S cancel() { ret "Request cancelled"; } bool shouldCancel() { false; } } sclass MainForm > FormInFlight { FormStep stepInterested, stepMailAddress, stepDevice, stepConfirm; *() { hashTag = "#mainForm"; steps.add(stepInterested = setAll(new StepInterested, key := "interested", desc := "Interested in free trial", displayText := "Are you after a free trial of our IPTV services?", buttons := ll("Yes, please!", "No, thanks."))); steps.add(stepMailAddress = nu FormStep( key := "mailAddress", desc := "E-mail address", displayText := "What is your e-mail address?")); steps.add(stepDevice = nu FormStep( key := "device", desc := "Device", displayText := "Which IPTV-capable device do you own?", buttons := ll("Android box", "Android phone", "Android tablet", "Firestick", "Smart TV", "None/Unsure"))); steps.add(stepConfirm = setAll(new StepConfirm, key := "confirm", placeholder := "", buttons := ll("Yes") )); } bool shouldCancel() { ret isNo(stepInterested.value); } class StepInterested extends FormStep { /*S handleAnswer(S s) { session->cancelForm(); }*/ } class StepConfirm extends FormStep { void update(Runnable onChange) { if (set_trueIfChanged(this, displayText := summary() + "\n\n" + "Submit contact request?")) callF(onChange); } } S summary() { print("Will send to: " + sendToViberIDs()); ret mapToLines_rtrim(listMinus(steps, stepConfirm), step -> (nempty(step.desc) ? addSuffix(step.desc, ":") : step.displayText) + " " + step.value); } LS sendToViberIDs() { ret llNempties(stefanViberID(), loadTextFileTrim(javaxSecretDir("iptv-viber-id.txt"); } S complete() { LS viberIDs = sendToViberIDs(); S text = summary() + "\n\n" + "[Form submitted in smart-iptv-solutions.co.uk chat bot, " + timeInTimeZoneWithDate_24(ukTimeZone_string()) + "]"; pcall { for (S viberID : sendToViberIDs()) viber_sendMessage(mandatoryViberToken(), "BotCompany", viberID, text); ret "Thank you, your contact request was sent!"; } ret "Sorry, an error occurred sending the message."; } S cancel() { ret "Contact request cancelled"; } } sbool debug; svoid setSession(S cookie, SS params) { session.set(uniq_sync(Session, +cookie)); S lang = mapGet(params, 'language); if (nempty(lang)) cset(session!, language := lang); S lang_default = mapGet(params, 'language_default); if (nempty(lang_default)) { print("lang_default=" + lang_default + ", have: " + session->language); if (empty(session->language)) { print("Setting language."); cset(session!, language := lang_default); } } } svoid clearSession(S cookie) { deleteConcepts(Session, +cookie); } p { db(); // for testing on desktop if (isMain()) { veryBigConsole(); bot(); } } sS returnQuestion(S q) { cset(session!, lastQuestion := q); ret q; } sS answer(S s) { if (session! == null) setSession("default", new Map); out.set(new Out); if (creator() == null) if "debug" set debug; S lq = session->lastQuestion; cset(session!, lastQuestion := null); FormInFlight form = session->form; if (form != null && form.currentStep() != null) { if (eqicOneOf(s, "cancel", "Abbrechen", unicode_crossProduct())) { S answer = form.cancel(); session->cancelForm(); ret answer; } else if (eqicOneOf(s, "back", "zurück", unicode_undoArrow()) && form.stepIndex > 0) { --form.stepIndex; session->change(); ret deliverAnswerAndFormStep(""); } else { FormStep step = form.currentStep(); if (nempty(step.buttons) && !cic(step.buttons, s)) ret deliverAnswerAndFormStep(de() ? "Bitte wählen Sie eine Option!" : "Please choose an option."); step.value = s; ++form.stepIndex; session->change(); if (form.currentStep() == null) { S answer = form.complete(); session->cancelForm(); ret answer; } ret deliverAnswerAndFormStep(""); } } if (testFunctions) { if (eq(lq, "Would you like some tea?")) { if "yes" ret "Here's your tea."; if "no" ret "Very well then."; } if "test buttons" { out->buttons = ll("Yes", "No"); ret returnQuestion("Would you like some tea?"); } if "say something later" { if (env == null) ret("No env"); final S mySession = session->cookie; thread { sleepSeconds(5); env.sayAsync(mySession, "Here it is."); } ret "OK, will do it in 5 seconds"; } } // get answer from bot, switch language O bot = dbBot(); S a = (S) call(bot, 'answer, s, session->language()); S lang = cast getThreadLocal(((ThreadLocal) get(bot, 'language_out))); if (nempty(lang)) cset(session!, language := lang); S a2 = replaceAll(a, "\\s*#mainForm\\b", ""); if (neq(a, a2)) { form = new MainForm; print("Made form for cookie " + session->cookie + ": " + sfu(form)); cset(session!, +form); } ret deliverAnswerAndFormStep(a2); } sS deliverAnswerAndFormStep(S answer) { FormInFlight form = session->form; if (form == null) ret answer; form.update(r { session->change() }); if (form.shouldCancel()) { session->cancelForm(); ret answer; } if (form.currentStep() == null) ret answer; FormStep step = form.currentStep(); if (empty(answer)) answer = step.displayText; else out->buttonsIntro = step.displayText; out->placeholder = or(step.placeholder, step.displayText); print("Step " + form.stepIndex + ": " + sfu(step)); out->defaultInput = or2(step.value, step.defaultValue); out->buttons = cloneList(step.buttons); if (form.stepIndex > 0) out->buttons.add(de() ? "Zurück" : "Back"); out->buttons.add(de() ? "Abbrechen" : "Cancel"); ret answer; } sS initialMessage() { print("initialMessage, lang=" + session->language()); ret or2(answer("#greeting"), "Hello"); } sO dbBot() { ret getBot(dbBotID); } sbool de() { ret eqic(session->language(), "de"); } sbool botOn() { ret isTrue(call(dbBot(), "botOn")); } sbool botAutoOpen() { ret isTrue(call(dbBot(), "botAutoOpen")); }