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

221
LINES

< > BotCompany Repo | #1022825 // Web Chat Bot Include 2 [dev.]

JavaX fragment (include)

abstract sclass ThoughtBot {
}

static ThoughtBot thoughtBot;
static int longPollTick = 100;
static int longPollMaxWait = 1000*60;

sclass Msg {
  long time;
  bool fromUser;
  S text;
  L<S> buttons;
  
  *() {}
  *(bool *fromUser, S *text) { time = now(); }
}

concept Conversation {
  S cookie;
  new LL<Msg> oldDialogs;
  new L<Msg> msgs;

  void add(Msg m) {
    msgs.add(m);
    change();
  }
  
  int allCount() { ret lengthLevel2(oldDialogs) + l(msgs); }
  int archiveSize() { ret lengthLevel2(oldDialogs); }
}

svoid pWebChatBot {
  db();
}

static void sayAsync(S session, S text) {
  lock dbLock();
  Conversation conv = getConv(session);
  conv.add(new Msg(false, text));
  print("sayAsync " + session + ", new size=" + conv.allCount());
}

html {
 _registerThread();
 registerVisitor();
 fS cookie = cookieSent();
 try {
  Conversation conv;
  {
    lock dbLock();
    if (eq(uri, "/stats"))
      ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads()));
      
    if (eq(uri, "/logs"))
      ret webChatBotLogsHTML();

    S message = trim(params.get("btn"));
    if (empty(message)) message = trim(params.get("message"));
    conv = getConv(cookie);
    
    if (match("clear", message)) {
      conv.oldDialogs.add(conv.msgs);
      cset(conv, msgs := new L);
      conv.change();
      thoughtBot.clearSession(conv.cookie);
      message = null;
    }
    
    thoughtBot.setSession(cookieSent());
    
    if (empty(conv.msgs))
      conv.add(new Msg(false, initialMessage()));
      
    if (nempty(message) && !lastUserMessageWas(conv, message))
      conv.add(new Msg(true, message));
  
    if (nempty(conv.msgs) && last(conv.msgs).fromUser) {
      S reply = "I'm sorry, I don't understand";
      L<S> buttons = null;
      pcall {
        reply = or2(makeReply(last(conv.msgs).text), reply);
        buttons = (L<S>) getThreadLocal(thoughtBot, "buttons");
      }
      Msg msg = new Msg(false, reply);
      msg.buttons = buttons;
      conv.add(msg);
    }
  } // locked
  
  if (eq(uri, "/msg")) ret "OK";
  
  if (eq(uri, "/incremental")) {
    int a = parseInt(params.get("a")), origA = a;
    //int b = parseInt(params.get("b"));
    int as = conv.archiveSize();
    a -= as;
    //b -= as;
    
    long start = sysNow();
    L msgs;
    bool first = true;
    while (licensed() && sysNow() < start+longPollMaxWait) {
      msgs = subList(conv.msgs, a);
      if (empty(msgs)) {
        if (first) {
          print("Long poll starting on " + cookie + ", " + origA + "/" + a);
          first = false;
        }
        sleep(longPollTick);
      } else {
        if (first) print("Long poll ended.");
        new StringBuilder buf;
        renderMessages(buf, msgs);
        ret "<!-- " + conv.allCount() + "-->\n"  + buf;
      }
    }
    ret "";
  }
  
  {
    lock dbLock();
    S html = loadSnippet(templateID); // TODO: cache
    new StringBuilder buf;
    
    renderMessages(buf, conv.msgs);
  
    html = html.replace("#N#", str(conv.allCount()));
    html = html.replace("#INCREMENTALURL#", relativeRawBotLink(programID(), "incremental?a=");
    html = html.replace("#MSGURL#", relativeRawBotLink(programID(), "msg?message="));
    if (nempty(params.get("debug")))
      html = html.replace("var showActions = false;", "var showActions = true;");
  
    html = html.replace("$HEADING", heading);
    html = html.replace("<!-- MSGS HERE -->", str(buf));
    html = hreplaceTitle(html, heading);
    html = hmobilefix(html);
    ret html;
  }
 } finally {
  _unregisterThread();
 }
}

svoid renderMessages(StringBuilder buf, L<Msg> msgs) {
  for (Msg m : msgs)
    if (m.fromUser || neq(m.text, "-"))
      appendMsg(buf, m.fromUser ? "Esteemed Customer" : botName, formatTime(m.time), htmlencode(m.text), !m.fromUser);
      
  L<S> buttons = last(msgs).buttons;
  if (nempty(buttons))
    appendButtons(buf, buttons);
}

svoid appendMsg(StringBuilder buf, S name, S time, S text, bool bot) {
  S imgLink = snippetImgLink(bot ? #1008323 : #1008359);
	buf.append([[<div class="direct-chat-msg doted-border">
    <div class="direct-chat-info clearfix">
    <span class="direct-chat-name pull-left">$NAME</span>
    </div>
    <img alt="message user image" src="$IMG" class="direct-chat-img">
    <div class="direct-chat-text">
      $TEXT
    </div>
    <div class="direct-chat-info clearfix">
      <span class="direct-chat-timestamp pull-right">$TIME</span>
    </div>
  </div>
    ]].replace("$IMG", imgLink)
      .replace("$NAME", name)
      .replace("$TIME", time)
      .replace("$TEXT", text));
}

svoid appendButtons(StringBuilder buf, L<S> buttons) {
  S buttonsHtml = lines(map(buttons, func(S text) {
    hsubmit(text, name := "btn")
  }));
	buf.append([[<div class="direct-chat-msg doted-border">
    <div class="direct-chat-buttons">
      $BUTTONS
    </div>
  </div>
    ]].replace("$BUTTONS", buttonsHtml);
}

svoid appendDate(StringBuilder buf, S date) {
  buf.append([[
  <div class="chat-box-single-line">
		<abbr class="timestamp">DATE</abbr>
	</div>]].replace("DATE", date));
}

sS initialMessage() {
  ret thoughtBot.initialMessage();
}

static bool lastUserMessageWas(Conversation conv, S message) {
  Msg m = last(conv.msgs);
  ret m != null && m.fromUser && eq(m.text, message);
}

sS makeReply(S message) {
  ret callStaticAnswerMethod(thoughtBot, message);
}

sS formatTime(long time) {
  ret formatGMT_24(time);
}

sS formatDialog(S id, L<Msg> msgs) {
  new L<S> lc;
  for (Msg m : msgs)
    lc.add(htmlencode((m.fromUser ? "> " : "< ") + m.text));
  ret id + ul(lc);
}

static Conversation getConv(fS cookie) {
  ret withDBLock(func -> Conversation {
    uniq(Conversation, +cookie)
  });
}

Author comment

Began life as a copy of #1008764

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1022825
Snippet name: Web Chat Bot Include 2 [dev.]
Eternal ID of this version: #1022825/1
Text MD5: 03b3b3f485bfb231a72ab7d4df45392e
Author: stefan
Category: javax / a.i.
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-04-09 20:17:28
Source code size: 5895 bytes / 221 lines
Pitched / IR pitched: No / No
Views / Downloads: 227 / 266
Referenced in: [show references]