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

223
LINES

< > BotCompany Repo | #1008764 // Web Chat Bot Include

JavaX fragment (include)

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

Author comment

Began life as a copy of #1008316

download  show line numbers  debug dex  old transpilations   

Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1008764
Snippet name: Web Chat Bot Include
Eternal ID of this version: #1008764/30
Text MD5: 1fa29ebce5c453a757504d821e5811c6
Author: stefan
Category: javax / a.i.
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-11-05 12:25:25
Source code size: 6074 bytes / 223 lines
Pitched / IR pitched: No / No
Views / Downloads: 771 / 1260
Version history: 29 change(s)
Referenced in: [show references]