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

495
LINES

< > BotCompany Repo | #1012236 // Stefan's Chat - pre parsing display

JavaX source code (desktop) - run with: x30.jar

Download Jar.

1  
!7
2  
3  
set flag NoNanoHTTPD.
4  
5  
sbool bottomRight = false;
6  
sS templateID = bottomRight ? #1008787 : /*#1009000*/#1009081;
7  
sS heading = "BotCompany | Stefan's Chat";
8  
sS roomName = "main"; // internal
9  
static int maxMessages = 40;
10  
static int visitors;
11  
static int messageLimit = stefansChat_messageLimit();
12  
sbool postingDisabled = false;
13  
static SS lastPosted = new ExpiringHashMap(2000); // ip -> message
14  
static volatile bool started;
15  
16  
static int longPollTick = 100;
17  
static int longPollMaxWait = 1000*60;
18  
19  
static new L pagePostProcessors;
20  
21  
concept WeirdIPs {
22  
  Set<S> ips = setInConcept(this, new TreeSet);
23  
}
24  
25  
concept Extension {
26  
  S code;
27  
  double priority;
28  
}
29  
30  
concept User {
31  
  S ipAddress, cookie;
32  
}
33  
34  
concept ByCookie {
35  
  S cookie;
36  
  S avatarID;
37  
}
38  
39  
sclass Msg {
40  
  S globalID = aGlobalIDUnlessLoading();
41  
  long time;
42  
  User user;
43  
  S text;
44  
  L<S> buttons;
45  
  bool botMark, auth;
46  
  int nr;
47  
  
48  
  *() {}
49  
  *(S ipAddress, S cookie, S *text) {
50  
    user = uniq_sync(User, +ipAddress, +cookie);
51  
    time = now();
52  
  }
53  
}
54  
55  
concept Conversation {
56  
  S cookie;
57  
  
58  
  // TODO: use synchro lists
59  
  new LL<Msg> oldDialogs;
60  
  new L<Msg> spam;
61  
  new L<Msg> msgs;
62  
  int counter;
63  
64  
  void add(Msg m) {
65  
    if (empty(oldDialogs)) oldDialogs.add(new L);
66  
    if (l(msgs) >= maxMessages)
67  
      last(oldDialogs).add(popFirst(msgs));
68  
    msgs.add(m);
69  
    change();
70  
    pcall { processMsgCommands(m); }
71  
  }
72  
  
73  
  // TODO
74  
  /*void correctSanity() {
75  
    if (empty(msgs)) ret;
76  
    int nr = last(msgs).nr;
77  
    int c = allCount();
78  
    while (nr > c) { msgs.add(Msg("null", "", "filler")); ++c; }
79  
    while (nr > c)
80  
  }*/
81  
  
82  
  void moveToSpam(Msg m, S toID) {
83  
    int i = msgs.indexOf(m);
84  
    if (i < 0) ret;
85  
    while (i < l(msgs) && neq(toID, msgs.get(i).globalID)) {
86  
      spam.add(msgs.get(i));
87  
      msgs.remove(i);
88  
      change();
89  
      if (empty(toID)) break;
90  
    }
91  
    moveMsgsUp();
92  
  }
93  
  
94  
  // move msgs from archive back to main dialog
95  
  void moveMsgsUp {
96  
    int delta = maxMessages-l(msgs);
97  
    print("moveMsgsUp delta=" + delta);
98  
    if (delta <= 0) ret;
99  
    L<Msg> old = last(oldDialogs);
100  
    if (old == null) ret;
101  
    L<Msg> l = takeLast(delta, old);
102  
    print("moveMsgsUp l=" + l(l));
103  
    if (empty(l)) ret;
104  
    msgs.addAll(0, l);
105  
    removeLast(old, delta);
106  
    change();
107  
  }
108  
  
109  
  int allCount() { ret archiveSize() + l(msgs); }
110  
  int archiveSize() { ret lengthLevel2(oldDialogs) + l(spam); }
111  
}
112  
113  
p {
114  
  loadPage_forcedTimeout = 10000;
115  
  dbIndexing(ByCookie, 'cookie);
116  
  started = true;
117  
  
118  
  for (Conversation conv)
119  
    print("room=" + conv.cookie + ": allCount=" + conv.allCount() + " old=" + l(conv.oldDialogs));
120  
    
121  
  L<Conversation> l = findConcepts(Conversation, cookie := roomName);
122  
  if (l(l) > 1) {
123  
    print("FIXING");
124  
    deleteConcept(smallestByMethod(l, 'allCount));
125  
  }
126  
  
127  
  for (Extension e : sortByField('priority, list(Extension)))
128  
    pcall { evalJava(e.code); }
129  
}
130  
131  
html {
132  
 _registerThread();
133  
 while (!started) sleep(100);
134  
 
135  
 try {
136  
  S _vis = registerVisitor();
137  
  int visitorsToday = parseFirstInt(_vis);
138  
  main.visitors = visitorsToday;
139  
  //fS cookie = cookieSent();
140  
  
141  
  if (eq(uri, "/thoughts")) try {
142  
    loadPage_extraHeaders.set(litmap("X-Forwarded-For", clientIP()));
143  
    ret hrefresh(10) + loadPage(smartBotURL() + "/thoughts"
144  
      + (webAuthed() ? "?_pretendAuthed=1" : ""));
145  
  } catch {
146  
    ret hrefresh(10) + hGoogleFontOswald() + hfullcenter("Bot loading...");
147  
  }
148  
    
149  
  new Matches mm;
150  
  Conversation conv;
151  
  {
152  
    lock dbLock();
153  
    
154  
    if (eq(uri, "/testauth")) ret "Authed: " + yn(webAuthed());
155  
    
156  
    if (swic(uri, "/setSmartBotURL/", mm)) {
157  
      if (!webAuthed()) ret "Not authed";
158  
      setSmartBotURL(urldecode(mm.rest()));
159  
      ret "OK";
160  
    }
161  
    
162  
    if (eq(uri, "/stats"))
163  
      ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads()));
164  
      
165  
    if (eq(uri, "/spam"))
166  
      ret h3AndTitle("Spam Log") + ul(reversedList(map htmlencode(scanLog("spam.log"))));
167  
      
168  
    if (eq(uri, "/logs"))
169  
      ret withDBLock(func -> S {
170  
        int step = 100, n = toInt(params.get("n"));
171  
        L<Msg> msgs = reversed(allMsgs()); // TODO: optimize
172  
        new L<S> l;
173  
        int count = l(msgs);
174  
        msgs = subList(msgs, n, n+step);
175  
        for (Msg m : msgs)
176  
          l.add(formatMsgForLog(m) + "<br>");
177  
        ret h3_htitle("Chat Logs") 
178  
          + pageNav2("/logs", count, n, step, 'n)
179  
        + p(join(reversed(l)));
180  
      });
181  
    
182  
    conv = getConv(roomName);
183  
    
184  
    if (eq(uri, "/sanity")) {
185  
      if (conv == null) ret "No conv";
186  
      Msg m = last(conv.msgs);
187  
      int c = conv.allCount();
188  
      if (m == null) ret "Msgs: " + l(conv.msgs) + " vs " + c;
189  
      ret m.nr + " vs " + c;
190  
    }
191  
  
192  
    S weirdip = params.get("weirdip");
193  
    if (nempty(weirdip) && webAuthed()) {
194  
      uniq(WeirdIPs).ips.add(weirdip);
195  
      ret "OK";
196  
    }
197  
    
198  
    S inspam = params.get("inspam");
199  
    S to = params.get("to");
200  
    if (possibleGlobalID(inspam) && webAuthed()) {
201  
      Msg m = findWhere(conv.msgs, globalID := inspam);
202  
      if (m == null) ret "Msg not found";
203  
      conv.moveToSpam(m, to);
204  
      ret "OK, moved to spam";
205  
    }
206  
207  
    S message = trim(params.get("btn"));
208  
    if (empty(message)) message = trim(params.get("message"));
209  
    bool botMark = nempty(params.get("botmark"));
210  
    //print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie);
211  
    
212  
    if (match("clear", message)) {
213  
      print("Clearing.");
214  
      conv.oldDialogs.add(conv.msgs);
215  
      cset(conv, msgs := new L);
216  
      conv.change();
217  
      message = null;
218  
    }
219  
    
220  
    if (nempty(message) /*&& !lastUserMessageWas(conv, message)*/)
221  
      postMessage(conv, message, botMark);
222  
  } // synchronized block
223  
  
224  
  if (eq(uri, "/msg")) ret "OK";
225  
  
226  
  if (eq(uri, "/n")) ret str(conv.allCount());
227  
  
228  
  if (eq(uri, "/lastmsg")) ret struct(msgsToJSON(takeLast(1, conv.msgs)));
229  
  
230  
  if (eq(uri, "/msgs-from-to")) {
231  
    int as = conv.archiveSize();
232  
    int a = parseInt(params.get('a))-as;
233  
    int b = parseInt(params.get('b))-as;
234  
    ret serveText(struct(msgsToJSON(subList(conv.msgs, a, b))));
235  
  }
236  
  
237  
  if (eq(uri, "/archive-from-to")) {
238  
    L<Msg> l = allMsgs();
239  
    int a = parseInt(params.get('a))-l(conv.spam);
240  
    int b = parseInt(params.get('b))-l(conv.spam);
241  
    ret serveText(struct(msgsToJSON(subList(l, a, b))));
242  
  }
243  
  
244  
  if (eq(uri, "/incremental")) {
245  
    if (uniq(WeirdIPs).ips.contains(clientIP())) {
246  
      print("Weird IP.");
247  
      sleepSeconds(10);
248  
    }
249  
    
250  
    int a = parseIntOpt(params.get("a"));
251  
    int vis = parseIntOpt(params.get("v"));
252  
    bool json = nempty(params.get('json)); // Funny thing is, JSON isn't even JSON. It's struct()
253  
254  
    long start = sysNow();
255  
    L msgs;
256  
    bool first = true;
257  
    while (licensed() && sysNow() < start+longPollMaxWait) {
258  
      int as = conv.archiveSize();
259  
      msgs = cloneSubList(conv.msgs, a-as);
260  
      //int visitors = main.visitors+random(2);
261  
      if (empty(msgs) && (vis == 0 || vis == visitors)) {
262  
        if (first) {
263  
          print("Long poll starting on " + roomName + ", " + a);
264  
          first = false;
265  
        }
266  
        sleep(longPollTick);
267  
      } else {
268  
        int ac = conv.allCount();
269  
        if (first) print("Long poll ended. a=" + a + ", as=" + as + ", msgs=" + l(msgs) + ", ac=" + ac);
270  
        if (json) ret struct/*jsonEncode*/(
271  
          litorderedmap(n := conv.allCount(),
272  
          msgs := msgsToJSON(msgs)));
273  
        new StringBuilder buf;
274  
        renderMessages(buf, msgs);
275  
        ret "<!-- " + ac + "-->\n"
276  
          + (vis == visitors ? "" : "<!-- vis " + visitors + " -->")
277  
          + buf;
278  
      }
279  
    }
280  
    ret "";
281  
  }
282  
  
283  
  lock dbLock();
284  
  
285  
  S html = loadSnippet_simpleCache(templateID);
286  
  new StringBuilder buf;
287  
  
288  
  print("Have " + n(conv.msgs, "msg"));
289  
  renderMessages(buf, conv.msgs);
290  
291  
  html = html.replace("#RELATIVESTYLES#", loadSnippet_simpleCache(#1010888));
292  
  html = html.replace("#N#", str(conv.allCount()));
293  
  html = html.replace("#VISITORS#", str(visitorsToday));
294  
  html = html.replace("#INCREMENTALURL#", relativeRawBotLink(programID(), "incremental");
295  
  html = html.replace("#MSGURL#", rawBotLink_rel(programID(), "msg?message="));
296  
  
297  
  // debug mode
298  
  html = html.replace("var showActions = false;", "var showActions = true;");
299  
  
300  
  int yw = 150, yh = 100;
301  
  
302  
  // MAKE MORE
303  
  
304  
  S more = 
305  
     h3("We make custom chat bots.")
306  
    + p(b(ahref("http://tinybrain.de/x30.jar", "The Software."))
307  
    + " (Windows/Linux/Mac OS.) Just double-click to run. " + targetBlank("http://java.com/", "Install Java if needed.") + "<br>"
308  
    + ahref("mailto:info@ai1.lol", "Mail.") + " "
309  
    + "Visitors today: " + span(visitorsToday, id := "visnum") + "." + "<br>"
310  
    + "Feel free to talk to " + b("Smart Bot") + " in this chat!" + " - " + targetBlank("http://tinybrain.de/1010745", "Smart Bot's Source Code.") + " "
311  
    + targetBlank("http://javax.tinybrain.de/", "JavaX.") + "<br>"
312  
    + htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://web-woody-lab.de/", "German smalltalk bot.") + " "
313  
    + htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://ai1.lol/1008316/raw", "Simple English product bot."))
314  
    + tag('table, tr(
315  
      td(youtubeEmbed("xeguVLToOwU", yw, yh))
316  
    + td(youtubeEmbed("z51GRYMSeaI", yw, yh), style := "padding-left: 10px")
317  
    + td(youtubeEmbed("jRb7UpwvJV8", yw, yh), style := "padding-left: 10px")
318  
    + td(youtubeEmbed("gOTPwVVyY-4", yw, yh), style := "padding-left: 10px")
319  
    ))
320  
    + h3("Bot's Thoughts " + small("(Type " + tt("!word ...") + " to change)"))
321  
    + tag('iframe, "", src := relativeRawBotLink(programID(), "thoughts"), width:= 700, height := 300)
322  
  ;
323  
324  
  html = html.replace("$HEADING", htmlencode(heading) + " | " + targetBlank(selfLink("logs"), "Log"));
325  
  html = html.replace("<!-- MSGS HERE -->", str(buf));
326  
  html = html.replace("<!--MORE STUFF HERE-->", more);
327  
  html = hreplaceTitle(html, heading);
328  
  html = hmobilefix(html);
329  
  for (O f : pagePostProcessors) pcall { html = or((S) callF(f, html), html); }
330  
  ret html;
331  
 } finally {
332  
  _unregisterThread();
333  
 }
334  
}
335  
336  
svoid renderMessages(StringBuilder buf, L<Msg> msgs) {
337  
  for (Msg m : msgs) {
338  
    new Matches mm;
339  
    S html;
340  
    if (startsWith(m.text, "[IMAGE] ", mm)) {
341  
      // IMAGE POST
342  
      S url = trim(mm.get(0));
343  
      html = targetBlank(url, himg(url, width := 200, title := "User-submitted image", style := "display: block; margin-left: auto; margin-right: auto"));
344  
    } else {
345  
      // TEXT POST
346  
      //dynamize_linkParams.set(new O[] { target := "_blank" });
347  
      //html = dynamize(m.text);
348  
      html = html_linkURLs_targetBlank(htmlEncode_nlToBr(m.text));
349  
    }
350  
    S name = "?";
351  
    if (m.user != null)
352  
      name = targetBlank("http://ai1.lol/1008750/?ip=" + urlencode(m.user.ipAddress), m.user.ipAddress, style := "color: white", title := "User's IP Address") + (nempty(m.user.cookie) ? " / " + targetBlank("http://ai1.lol/" + m.user.cookie, m.user.cookie, style := "color: white", title := "User's Cookie") : "")
353  
        + " | " + targetBlank(/*"http://ai1.lol/" + m.globalID*/encyclopediaLink("Chat line " + m.nr),
354  
          /*m.globalID*/m.nr, style := "color: white", title := "Message ID");
355  
    if (webAuthed()) name += " " + targetBlank(botLink(programID()) + "?inspam=" + m.globalID, "[spam]");
356  
    renderMessage(buf, name, formatTime(m.time), html, m.globalID, m.user == null ? null : m.user.cookie, m.botMark, m.nr);
357  
  }
358  
      
359  
  if (empty(msgs)) ret;
360  
  L<S> buttons = last(msgs).buttons;
361  
  if (nempty(buttons))
362  
    appendButtons(buf, buttons);
363  
}
364  
365  
static O msgsToJSON(L<Msg> msgs) {
366  
  ret map(msgs, func(Msg m) { litorderedmap(
367  
    time := m.time,
368  
    text := m.text,
369  
    ip := m.user.ipAddress,
370  
    cookie := m.user.cookie,
371  
    buttons := m.buttons,
372  
    auth := trueOrNull(m.auth),
373  
    botMark := trueOrNull(m.botMark),
374  
    nr := m.nr,
375  
    globalID := m.globalID
376  
  ) });
377  
}
378  
379  
svoid renderMessage(StringBuilder buf, S name, S time, S html, S id, S cookie, bool botMark, int nr) {
380  
  ByCookie bc = findConcept(ByCookie, +cookie);
381  
  S imgID = botMark ? #1008323 : #1008359;
382  
  if (nempty(cookie) && !botMark /*XXX*/ && bc != null) imgID = or(bc.avatarID, imgID);
383  
  S imgLink = snippetImgLink(imgID);
384  
  //if (nempty(id)) buf.append(hcomment("Msg ID: " + id));
385  
	buf.append([[<div class="direct-chat-msg doted-border" id="msg_#ID#">
386  
    <div class="direct-chat-info clearfix">
387  
    <span class="direct-chat-name pull-left">$NAME</span>
388  
    </div>
389  
    <img alt="message user image" src="$IMG" class="direct-chat-img">
390  
    <div class="direct-chat-text" $STYLE>
391  
      $TEXT
392  
    </div>
393  
    <div class="direct-chat-info clearfix">
394  
      <span class="direct-chat-timestamp pull-right">$TIME</span>
395  
    </div>
396  
  </div>
397  
    ]].replace("$IMG", imgLink)
398  
      .replace("$NAME", name)
399  
      .replace("$TIME", time)
400  
      .replace("#ID#", id)
401  
      .replace("$STYLE", botMark
402  
        //? "style='text-align: right'"
403  
        //? "style='font-style: italic'"
404  
        ? "style='font-family: Pacifico'"
405  
        : "")
406  
      .replace("$TEXT", html));
407  
}
408  
409  
svoid appendButtons(StringBuilder buf, L<S> buttons) {
410  
  S buttonsHtml = lines(map(buttons, func(S text) {
411  
    hsubmit(text, name := "btn")
412  
  }));
413  
	buf.append([[<div class="direct-chat-msg doted-border">
414  
    <div class="direct-chat-buttons">
415  
      $BUTTONS
416  
    </div>
417  
  </div>
418  
    ]].replace("$BUTTONS", buttonsHtml);
419  
}
420  
421  
svoid appendDate(StringBuilder buf, S date) {
422  
  buf.append([[
423  
  <div class="chat-box-single-line">
424  
		<abbr class="timestamp">DATE</abbr>
425  
	</div>]].replace("DATE", date));
426  
}
427  
428  
sS formatTime(long time) {
429  
  ret formatGMTWithOptionalDate_24(time);
430  
}
431  
432  
sS formatMsgForLog(Msg m) {
433  
  ret htmlencode(m.nr + " " + formatDateAndTime(m.time)) + " - " + htmlencode(m.user.ipAddress + ": " + m.text);
434  
}
435  
436  
static Conversation getConv(fS cookie) {
437  
  ret uniq_sync(Conversation, +cookie);
438  
}
439  
440  
static L<Msg> allMsgs() {
441  
  new L<Msg> l;
442  
  for (Conversation c) {
443  
    for (L<Msg> msgs : c.oldDialogs) l.addAll(msgs);
444  
    l.addAll(c.msgs);
445  
  }
446  
  ret l;
447  
}
448  
449  
svoid processMsgCommands(Msg msg) {
450  
  new Matches m;
451  
  if (swic(msg.text, "avatar ", m)) {
452  
    S avatarID = fsI(trim($1));
453  
    BufferedImage img = loadImage2(avatarID);
454  
    if (img.getWidth() <= 400 && img.getHeight() <= 400)
455  
      cset(uniq(ByCookie, cookie := msg.user.cookie), +avatarID);
456  
    else fail("Avatar too big: " + avatarID);
457  
  }
458  
}
459  
460  
svoid postMessage(Conversation conv, S message, bool botMark) {
461  
  message = shorten(message, messageLimit, " [...]");
462  
  S ip = clientIP();
463  
  print("Have message from " + ip + " at " + gmtWithSeconds() + " in thread " + currentThread() + ": " + quote(shorten(message, 80)));
464  
  
465  
  if (startsWith(ip, "66.249.65."))
466  
    if (matchOneOf(message, "web * is invalid", "web * is correct"))
467  
      ret;
468  
  
469  
  // anti hammer mechanism
470  
  S key = ip + " " + botMark;
471  
  if (eq(lastPosted.get(key), message)) {
472  
    print("Skipping same.");
473  
    ret;
474  
  }
475  
  
476  
  lastPosted.put(key, message);
477  
    
478  
  if (postingDisabled) ret;
479  
  
480  
  if (superSimpleSpamTester(message)) {
481  
    print(logStructureWithDate("spam.log", "Ignoring spam message: " + quote(message)));
482  
    ret;
483  
  }
484  
  
485  
  // ADD NOT SPAM MESSAGE
486  
  Msg msg = new Msg(clientIP(), cookieConcept(), message);
487  
  msg.botMark = botMark;
488  
  msg.auth = webAuthed();
489  
  //msg.nr = toInt(getOpt(last(conv.msgs), 'nr))+1;
490  
  if (conv.counter == 0) cset(conv, counter := conv.allCount());
491  
  cset(conv, counter := conv.counter+1);
492  
  msg.nr = conv.counter;
493  
  conv.add(msg);
494  
  print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie + " after add");
495  
}

Author comment

Began life as a copy of #1008998

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: #1012236
Snippet name: Stefan's Chat - pre parsing display
Eternal ID of this version: #1012236/1
Text MD5: edb7f1ba11c2585fb5788906fe0badfb
Author: stefan
Category: javax / a.i.
Type: JavaX source code (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2017-11-26 11:27:19
Source code size: 15876 bytes / 495 lines
Pitched / IR pitched: No / No
Views / Downloads: 422 / 522
Referenced in: [show references]