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)

1  
abstract sclass ThoughtBot {
2  
}
3  
4  
static ThoughtBot thoughtBot;
5  
static int longPollTick = 100;
6  
static int longPollMaxWait = 1000*60;
7  
8  
sclass Msg {
9  
  long time;
10  
  bool fromUser;
11  
  S text;
12  
  L<S> buttons;
13  
  
14  
  *() {}
15  
  *(bool *fromUser, S *text) { time = now(); }
16  
}
17  
18  
concept Conversation {
19  
  S cookie;
20  
  new LL<Msg> oldDialogs;
21  
  new L<Msg> msgs;
22  
23  
  void add(Msg m) {
24  
    msgs.add(m);
25  
    change();
26  
  }
27  
  
28  
  int allCount() { ret lengthLevel2(oldDialogs) + l(msgs); }
29  
  int archiveSize() { ret lengthLevel2(oldDialogs); }
30  
}
31  
32  
svoid pWebChatBot {
33  
  db();
34  
}
35  
36  
static void sayAsync(S session, S text) {
37  
  lock dbLock();
38  
  Conversation conv = getConv(session);
39  
  conv.add(new Msg(false, text));
40  
  print("sayAsync " + session + ", new size=" + conv.allCount());
41  
}
42  
43  
html {
44  
 _registerThread();
45  
 registerVisitor();
46  
 fS cookie = cookieSent();
47  
 try {
48  
  Conversation conv;
49  
  {
50  
    lock dbLock();
51  
    if (eq(uri, "/stats"))
52  
      ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads()));
53  
      
54  
    if (eq(uri, "/logs"))
55  
      ret webChatBotLogsHTML();
56  
57  
    S message = trim(params.get("btn"));
58  
    if (empty(message)) message = trim(params.get("message"));
59  
    conv = getConv(cookie);
60  
    
61  
    if (match("clear", message)) {
62  
      conv.oldDialogs.add(conv.msgs);
63  
      cset(conv, msgs := new L);
64  
      conv.change();
65  
      thoughtBot.clearSession(conv.cookie);
66  
      message = null;
67  
    }
68  
    
69  
    thoughtBot.setSession(cookieSent());
70  
    
71  
    if (empty(conv.msgs))
72  
      conv.add(new Msg(false, initialMessage()));
73  
      
74  
    if (nempty(message) && !lastUserMessageWas(conv, message))
75  
      conv.add(new Msg(true, message));
76  
  
77  
    if (nempty(conv.msgs) && last(conv.msgs).fromUser) {
78  
      S reply = "I'm sorry, I don't understand";
79  
      L<S> buttons = null;
80  
      pcall {
81  
        reply = or2(makeReply(last(conv.msgs).text), reply);
82  
        buttons = (L<S>) getThreadLocal(thoughtBot, "buttons");
83  
      }
84  
      Msg msg = new Msg(false, reply);
85  
      msg.buttons = buttons;
86  
      conv.add(msg);
87  
    }
88  
  } // locked
89  
  
90  
  if (eq(uri, "/msg")) ret "OK";
91  
  
92  
  if (eq(uri, "/incremental")) {
93  
    int a = parseInt(params.get("a")), origA = a;
94  
    //int b = parseInt(params.get("b"));
95  
    int as = conv.archiveSize();
96  
    a -= as;
97  
    //b -= as;
98  
    
99  
    long start = sysNow();
100  
    L msgs;
101  
    bool first = true;
102  
    while (licensed() && sysNow() < start+longPollMaxWait) {
103  
      msgs = subList(conv.msgs, a);
104  
      if (empty(msgs)) {
105  
        if (first) {
106  
          print("Long poll starting on " + cookie + ", " + origA + "/" + a);
107  
          first = false;
108  
        }
109  
        sleep(longPollTick);
110  
      } else {
111  
        if (first) print("Long poll ended.");
112  
        new StringBuilder buf;
113  
        renderMessages(buf, msgs);
114  
        ret "<!-- " + conv.allCount() + "-->\n"  + buf;
115  
      }
116  
    }
117  
    ret "";
118  
  }
119  
  
120  
  {
121  
    lock dbLock();
122  
    S html = loadSnippet(templateID); // TODO: cache
123  
    new StringBuilder buf;
124  
    
125  
    renderMessages(buf, conv.msgs);
126  
  
127  
    html = html.replace("#N#", str(conv.allCount()));
128  
    html = html.replace("#INCREMENTALURL#", relativeRawBotLink(programID(), "incremental?a=");
129  
    html = html.replace("#MSGURL#", relativeRawBotLink(programID(), "msg?message="));
130  
    if (nempty(params.get("debug")))
131  
      html = html.replace("var showActions = false;", "var showActions = true;");
132  
  
133  
    html = html.replace("$HEADING", heading);
134  
    html = html.replace("<!-- MSGS HERE -->", str(buf));
135  
    html = hreplaceTitle(html, heading);
136  
    html = hmobilefix(html);
137  
    ret html;
138  
  }
139  
 } finally {
140  
  _unregisterThread();
141  
 }
142  
}
143  
144  
svoid renderMessages(StringBuilder buf, L<Msg> msgs) {
145  
  for (Msg m : msgs)
146  
    if (m.fromUser || neq(m.text, "-"))
147  
      appendMsg(buf, m.fromUser ? "Esteemed Customer" : botName, formatTime(m.time), htmlencode(m.text), !m.fromUser);
148  
      
149  
  L<S> buttons = last(msgs).buttons;
150  
  if (nempty(buttons))
151  
    appendButtons(buf, buttons);
152  
}
153  
154  
svoid appendMsg(StringBuilder buf, S name, S time, S text, bool bot) {
155  
  S imgLink = snippetImgLink(bot ? #1008323 : #1008359);
156  
	buf.append([[<div class="direct-chat-msg doted-border">
157  
    <div class="direct-chat-info clearfix">
158  
    <span class="direct-chat-name pull-left">$NAME</span>
159  
    </div>
160  
    <img alt="message user image" src="$IMG" class="direct-chat-img">
161  
    <div class="direct-chat-text">
162  
      $TEXT
163  
    </div>
164  
    <div class="direct-chat-info clearfix">
165  
      <span class="direct-chat-timestamp pull-right">$TIME</span>
166  
    </div>
167  
  </div>
168  
    ]].replace("$IMG", imgLink)
169  
      .replace("$NAME", name)
170  
      .replace("$TIME", time)
171  
      .replace("$TEXT", text));
172  
}
173  
174  
svoid appendButtons(StringBuilder buf, L<S> buttons) {
175  
  S buttonsHtml = lines(map(buttons, func(S text) {
176  
    hsubmit(text, name := "btn")
177  
  }));
178  
	buf.append([[<div class="direct-chat-msg doted-border">
179  
    <div class="direct-chat-buttons">
180  
      $BUTTONS
181  
    </div>
182  
  </div>
183  
    ]].replace("$BUTTONS", buttonsHtml);
184  
}
185  
186  
svoid appendDate(StringBuilder buf, S date) {
187  
  buf.append([[
188  
  <div class="chat-box-single-line">
189  
		<abbr class="timestamp">DATE</abbr>
190  
	</div>]].replace("DATE", date));
191  
}
192  
193  
sS initialMessage() {
194  
  ret thoughtBot.initialMessage();
195  
}
196  
197  
static bool lastUserMessageWas(Conversation conv, S message) {
198  
  Msg m = last(conv.msgs);
199  
  ret m != null && m.fromUser && eq(m.text, message);
200  
}
201  
202  
sS makeReply(S message) {
203  
  ret callStaticAnswerMethod(thoughtBot, message);
204  
}
205  
206  
sS formatTime(long time) {
207  
  ret formatGMT_24(time);
208  
}
209  
210  
sS formatDialog(S id, L<Msg> msgs) {
211  
  new L<S> lc;
212  
  for (Msg m : msgs)
213  
    lc.add(htmlencode((m.fromUser ? "> " : "< ") + m.text));
214  
  ret id + ul(lc);
215  
}
216  
217  
static Conversation getConv(fS cookie) {
218  
  ret withDBLock(func -> Conversation {
219  
    uniq(Conversation, +cookie)
220  
  });
221  
}

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: 229 / 268
Referenced in: [show references]