Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

321
LINES

< > BotCompany Repo | #1026410 - Tomii Boi Thought Bot [LIVE]

JavaX source code (desktop) [tags: use-pretranspiled] - run with: x30.jar

Libraryless. Click here for Pure Java version (14003L/99K).

!7

sS dbBotID = #1026409;

static Env env;
static new ThreadLocal<Session> session;
static new ThreadLocal<Out> 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;
  bool allowFreeText; // only matters when there are buttons
  S value;
  
  // called before data entry
  void update(Runnable onChange) {}

  // called after data entry
  // return error message or null
  // call session->cancelForm(); to cancel the form (and make sure to return a text)
  S verifyData(S s) { null; }
}

sclass FormInFlight {
  S hashTag;
  new L<FormStep> 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"; }
}

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."),
      allowFreeText := true));
      
    steps.add(stepMailAddress = setAll(new StepMail,
      key := "mailAddress",
      desc := "E-mail address",
      displayText := "Please provide your e-mail address so we can contact you!",
      placeholder := "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"),
      allowFreeText := true));
      
    steps.add(stepConfirm = setAll(new StepConfirm,
      key := "confirm",
      placeholder := "",
      buttons := ll("Yes")
    ));
  }
  
  class StepInterested extends FormStep {
    S verifyData(S s) {
      s = trim(s);
      if (isNo(s) || matchStart("not", s)) {
        session->cancelForm();
        ret answer("#notInterestedInTrial");
      }
      if (!isYes(s)) {
        // cancel form, interpret as normal input
        session->cancelForm();
        ret answer(s);
      }
      null;
    }
  }
  
  class StepMail extends FormStep {
    @Override
    S verifyData(S s) {
      ret callPrint1x isValidEmailAddress_simple('isValidEmailAddress_simple, trim(s)) ? null : "Please enter a valid email address.";
    }
  }
  
  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(loadTextFileTrim(javaxSecretDir("iptv-viber-id.txt")),
      stefanViberID());
  }
  
  S complete() {
    LS viberIDs = sendToViberIDs();
    S text = joinWithEmptyLines(
      "Contact request over smart-iptv-solutions.co.uk (" + timeInTimeZoneWithDate_24(ukTimeZone_string()) + ")",
      summary());
    pcall {
      for (S viberID : sendToViberIDs())
        viber_sendMessage(mandatoryViberToken(),
          /*"BotCompany"*/"smart-iptv-solutions.co.uk",
          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 (!step.allowFreeText && nempty(step.buttons) && !cic(step.buttons, s))
        ret deliverAnswerAndFormStep(de() ? "Bitte wählen Sie eine Option!" : "Please choose an option.");
      print("Verifying data " + quote(s) + " in step " + step);
      S error = step.verifyData(s);
      if (error != null)
        ret deliverAnswerAndFormStep(error);

      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) {
  if (session->form != null) session->form.update(r { session->change() });
  /*if (form.shouldCancel()) {
    session->cancelForm();
    ret answer;
  }
*/
  FormInFlight form = session->form; // form may have cancelled itself in update
  if (form == null || 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"));
}

Author comment

Began life as a copy of #1026222

download  show line numbers  debug dex   

Travelled to 2 computer(s): mqqgnosmbjvj, tvejysmllsmz

No comments. add comment

Snippet ID: #1026410
Snippet name: Tomii Boi Thought Bot [LIVE]
Eternal ID of this version: #1026410/1
Text MD5: 018d79e4f3780dffc7dd2095d188c62b
Transpilation MD5: 4050651961a71c7af1b9c4af2da71a81
Author: stefan
Category: javax / a.i.
Type: JavaX source code (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-01-03 00:55:44
Source code size: 8803 bytes / 321 lines
Pitched / IR pitched: No / No
Views / Downloads: 14 / 51
Referenced in: [show references]

Formerly at http://tinybrain.de/1026410 & http://1026410.tinybrain.de