1 | set flag NoNanoHTTPD. |
2 | |
3 | static O stylesPost; // func(S) -> S |
4 | |
5 | sS botFont = "Righteous"; //"Inconsolata"; //"Pacifico"; |
6 | sS botFontWeight = "normal"; |
7 | sS botImageID = #1101140; //#1008323; |
8 | sbool bottomRight = false; |
9 | sS templateID = bottomRight ? #1008787 : /*#1009000*/#1011983; |
10 | static F0<S> headerHTML; |
11 | sS heading = "BotCompany | Stefan's Chat"; |
12 | sbool forceHttps; |
13 | sS roomName = "main"; // internal |
14 | sS chatLineSymbol = "Chat line"; |
15 | static int maxMessages = 40; |
16 | static int visitors; |
17 | static int messageLimit = stefansChat_messageLimit(); |
18 | sbool postingDisabled = false; |
19 | sbool showParses = true, notifications; |
20 | static SS lastPosted = new ExpiringHashMap(2000); // ip -> message |
21 | static volatile bool started; |
22 | static O parserModule; |
23 | |
24 | static int longPollTick = 100; |
25 | static int longPollMaxWait = 1000*60; |
26 | |
27 | static new L pagePostProcessors; |
28 | |
29 | concept WeirdIPs { |
30 | Set<S> ips = setInConcept(this, new TreeSet); |
31 | } |
32 | |
33 | concept User { |
34 | S ipAddress, cookie; |
35 | } |
36 | |
37 | concept ByCookie { |
38 | S cookie; |
39 | S avatarID; |
40 | } |
41 | |
42 | sclass Msg { |
43 | S globalID = aGlobalIDUnlessLoading(); |
44 | long time; |
45 | User user; |
46 | S text, parse; |
47 | Bool goodParse; |
48 | L<S> buttons; |
49 | bool botMark, auth; |
50 | int nr; |
51 | |
52 | *() {} |
53 | *(S ipAddress, S cookie, S *text) { |
54 | user = uniq_sync(User, +ipAddress, +cookie); |
55 | time = now(); |
56 | } |
57 | } |
58 | |
59 | concept Conversation { |
60 | S cookie; |
61 | |
62 | // TODO: use synchro lists |
63 | new LL<Msg> oldDialogs; |
64 | new L<Msg> spam; |
65 | new L<Msg> msgs; |
66 | int counter; |
67 | |
68 | void add(Msg m) { |
69 | if (empty(oldDialogs)) oldDialogs.add(new L); |
70 | if (l(msgs) >= maxMessages) |
71 | listAdd(last(oldDialogs), popFirst(msgs)); |
72 | listAdd(msgs, m); |
73 | change(); |
74 | pcall { processMsgCommands(m); } |
75 | } |
76 | |
77 | // TODO |
78 | /*void correctSanity() { |
79 | if (empty(msgs)) ret; |
80 | int nr = last(msgs).nr; |
81 | int c = allCount(); |
82 | while (nr > c) { msgs.add(Msg("null", "", "filler")); ++c; } |
83 | while (nr > c) |
84 | }*/ |
85 | |
86 | void moveToSpam(Msg m, S toID) { |
87 | int i = msgs.indexOf(m); |
88 | if (i < 0) ret; |
89 | while (i < l(msgs) && neq(toID, msgs.get(i).globalID)) { |
90 | spam.add(msgs.get(i)); |
91 | msgs.remove(i); |
92 | change(); |
93 | if (empty(toID)) break; |
94 | } |
95 | moveMsgsUp(); |
96 | } |
97 | |
98 | // move msgs from archive back to main dialog |
99 | void moveMsgsUp { |
100 | int delta = maxMessages-l(msgs); |
101 | print("moveMsgsUp delta=" + delta); |
102 | if (delta <= 0) ret; |
103 | L<Msg> old = last(oldDialogs); |
104 | if (old == null) ret; |
105 | L<Msg> l = takeLast(delta, old); |
106 | print("moveMsgsUp l=" + l(l)); |
107 | if (empty(l)) ret; |
108 | synchronized(msgs) { msgs.addAll(0, l); } |
109 | removeLast(old, delta); |
110 | change(); |
111 | } |
112 | |
113 | int allCount() { ret archiveSize() + l(msgs); } |
114 | int archiveSize() { ret lengthLevel2(oldDialogs) + l(spam); } |
115 | } |
116 | |
117 | svoid stefansChat_init { |
118 | loadPage_forcedTimeout = 20000; |
119 | dbIndexing(ByCookie, 'cookie); |
120 | started = true; |
121 | |
122 | for (Conversation conv) |
123 | print("room=" + conv.cookie + ": allCount=" + conv.allCount() + " old=" + l(conv.oldDialogs)); |
124 | |
125 | L<Conversation> l = findConcepts(Conversation, cookie := roomName); |
126 | if (l(l) > 1) { |
127 | print("FIXING"); |
128 | deleteConcept(smallestByMethod(l, 'allCount)); |
129 | } |
130 | } |
131 | |
132 | html { |
133 | _registerThread(); |
134 | while (!started) sleep(100); |
135 | |
136 | try { |
137 | S _vis = registerVisitor(); |
138 | int visitorsToday = parseFirstInt(_vis); |
139 | main.visitors = visitorsToday; |
140 | //fS cookie = cookieSent(); |
141 | |
142 | bool authed = webAuthed(params); |
143 | |
144 | print("uri=" + uri); |
145 | if (forceHttps && eq(uri, "/")) { |
146 | int port = subBot_currentPort(); |
147 | print("Port=" + port); |
148 | if (port != 0 && port != 443) |
149 | ret hrefresh("https://" + domain()); |
150 | } |
151 | |
152 | if (eq(uri, "/thoughts")) try { |
153 | loadPage_extraHeaders.set(litmap("X-Forwarded-For", clientIP())); |
154 | ret hrefresh(10) + loadPage(smartBotURL() + "/thoughts" |
155 | + (authed ? "?_pretendAuthed=1" : "")); |
156 | } catch e { |
157 | printShortException(e); |
158 | ret hrefresh(10) + hGoogleFontOswald() + hfullcenter("Bot loading..."); |
159 | } |
160 | |
161 | new Matches mm; |
162 | Conversation conv; |
163 | { |
164 | lock dbLock(); |
165 | |
166 | if (eq(uri, "/testauth")) ret "Authed: " + yn(authed); |
167 | |
168 | if (eq(uri, "/refresh")) { |
169 | if (!authed) ret "Not authed"; |
170 | lock downloadLock(); |
171 | parserModule = null; |
172 | ret "OK"; |
173 | } |
174 | |
175 | if (swic(uri, "/setSmartBotURL/", mm)) { |
176 | if (!authed) ret "Not authed"; |
177 | setSmartBotURL(urldecode(mm.rest())); |
178 | ret "OK"; |
179 | } |
180 | |
181 | if (eq(uri, "/stats")) |
182 | ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads())); |
183 | |
184 | if (eq(uri, "/spam")) |
185 | ret h3AndTitle("Spam Log") + ul(reversedList(map htmlencode(scanLog("spam.log")))); |
186 | |
187 | if (eq(uri, "/logs")) |
188 | ret withDBLock(func -> S { |
189 | int step = 100, n = toInt(params.get("n")); |
190 | L<Msg> msgs = reversed(allMsgs()); // TODO: optimize |
191 | new L<S> l; |
192 | int count = l(msgs); |
193 | msgs = subList(msgs, n, n+step); |
194 | for (Msg m : msgs) |
195 | l.add(formatMsgForLog(m) + "<br>"); |
196 | ret h3_htitle("Chat Logs") |
197 | + pageNav2("/logs", count, n, step, 'n) |
198 | + p(join(reversed(l))); |
199 | }); |
200 | |
201 | conv = getConv(roomName); |
202 | |
203 | if (eq(uri, "/sanity")) { |
204 | if (conv == null) ret "No conv"; |
205 | Msg m = last(conv.msgs); |
206 | int c = conv.allCount(); |
207 | if (m == null) ret "Msgs: " + l(conv.msgs) + " vs " + c; |
208 | ret m.nr + " vs " + c; |
209 | } |
210 | |
211 | S weirdip = params.get("weirdip"); |
212 | if (nempty(weirdip) && authed) { |
213 | uniq(WeirdIPs).ips.add(weirdip); |
214 | ret "OK"; |
215 | } |
216 | |
217 | S inspam = params.get("inspam"); |
218 | S to = params.get("to"); |
219 | if (possibleGlobalID(inspam) && authed) { |
220 | Msg m = findWhere(conv.msgs, globalID := inspam); |
221 | if (m == null) ret "Msg not found"; |
222 | conv.moveToSpam(m, to); |
223 | ret "OK, moved to spam"; |
224 | } |
225 | |
226 | S rateParse = params.get("rateParse"); |
227 | if (possibleGlobalID(rateParse) && authed) { |
228 | bool value = eq("1", params.get("ok")); |
229 | Msg m = findWhere(conv.msgs, globalID := rateParse); |
230 | if (m == null) ret "Msg not found"; |
231 | m.goodParse = value; |
232 | change(); |
233 | ret "OK, rated " + (value ? "good" : "bad"); |
234 | } |
235 | |
236 | S reparse = params.get("reparse"); |
237 | if (possibleGlobalID(reparse)) { |
238 | Msg m = findWhere(conv.msgs, globalID := reparse); |
239 | if (m == null) ret "Msg not found"; |
240 | S parse = m.parse; |
241 | parseMsg(m); |
242 | bool change = neq(parse, m.parse); |
243 | if (change) { |
244 | m.goodParse = null; |
245 | change(); |
246 | } |
247 | ret change ? htmlencode("OK -> " + m.parse) : "No change"; |
248 | } |
249 | |
250 | if (eq(uri, "/parse-all") && authed) { |
251 | time { parseAll(); } |
252 | ret "OK, " + lastTiming() + " ms"; |
253 | } |
254 | |
255 | S message = trim(params.get("btn")); |
256 | if (empty(message)) message = trim(params.get("message")); |
257 | bool botMark = nempty(params.get("botmark")); |
258 | //print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie); |
259 | |
260 | if (match("clear", message)) { |
261 | print("Clearing."); |
262 | conv.oldDialogs.add(conv.msgs); |
263 | cset(conv, msgs := new L); |
264 | conv.change(); |
265 | message = null; |
266 | } |
267 | |
268 | if (nempty(message) /*&& !lastUserMessageWas(conv, message)*/) |
269 | postMessage(conv, message, botMark, authed); |
270 | } // synchronized block |
271 | |
272 | if (eq(uri, "/msg")) ret "OK"; |
273 | |
274 | if (eq(uri, "/n")) ret str(conv.allCount()); |
275 | |
276 | if (eq(uri, "/lastmsg")) ret struct(msgsToJSON(takeLast(1, conv.msgs))); |
277 | |
278 | if (eq(uri, "/msgs-from-to")) { |
279 | int as = conv.archiveSize(); |
280 | int a = parseInt(params.get('a))-as; |
281 | int b = parseInt(params.get('b))-as; |
282 | ret serveText(struct(msgsToJSON(subList(conv.msgs, a, b)))); |
283 | } |
284 | |
285 | if (eq(uri, "/archive-from-to")) { |
286 | L<Msg> l = allMsgs(); |
287 | int a = parseInt(params.get('a))-l(conv.spam); |
288 | int b = parseInt(params.get('b))-l(conv.spam); |
289 | ret serveText(struct(msgsToJSON(subList(l, a, b)))); |
290 | } |
291 | |
292 | if (eq(uri, "/archive-regexp")) { |
293 | fS regexp = params.get("regexp"); |
294 | fL<Msg> l = allMsgs(); |
295 | ret serveText(struct(msgsToJSON(evalWithTimeoutOrNull(10.0, func -> L<Msg> { |
296 | Pattern pat = regexpIC(regexp); |
297 | new L<Msg> l2; |
298 | for (Msg m : l) |
299 | if (safeRegexpFind(pat, m.text)) |
300 | l2.add(m); |
301 | ret l2; |
302 | })))); |
303 | } |
304 | |
305 | if (eq(uri, "/incremental")) { |
306 | if (uniq(WeirdIPs).ips.contains(clientIP())) { |
307 | print("Weird IP."); |
308 | sleepSeconds(10); |
309 | } |
310 | |
311 | int a = parseIntOpt(params.get("a")); |
312 | int vis = parseIntOpt(params.get("v")); |
313 | bool json = nempty(params.get('json)); // Funny thing is, JSON isn't even JSON. It's struct() |
314 | |
315 | long start = sysNow(); |
316 | L msgs; |
317 | bool first = true; |
318 | while (licensed() && sysNow() < start+longPollMaxWait) { |
319 | int as = conv.archiveSize(); |
320 | msgs = cloneSubList(conv.msgs, a-as); |
321 | //int visitors = main.visitors+random(2); |
322 | if (empty(msgs) && (vis == 0 || vis == visitors)) { |
323 | if (first) { |
324 | print("Long poll starting on " + roomName + ", " + a); |
325 | first = false; |
326 | } |
327 | sleep(longPollTick); |
328 | } else { |
329 | int ac = conv.allCount(); |
330 | if (first) print("Long poll ended. a=" + a + ", as=" + as + ", msgs=" + l(msgs) + ", ac=" + ac); |
331 | if (json) ret struct/*jsonEncode*/( |
332 | litorderedmap(n := conv.allCount(), |
333 | msgs := msgsToJSON(msgs))); |
334 | new StringBuilder buf; |
335 | renderMessages(buf, msgs); |
336 | ret "<!-- " + ac + "-->\n" |
337 | + (vis == visitors ? "" : "<!-- vis " + visitors + " -->") |
338 | + buf; |
339 | } |
340 | } |
341 | ret ""; |
342 | } |
343 | |
344 | lock dbLock(); |
345 | |
346 | S html = loadSnippet_simpleCache(templateID); |
347 | S headerHTML = callF(main.headerHTML); |
348 | if (headerHTML != null) |
349 | html = replacePartBetweenStrings(html, "<!-- HEAD -->", "<!-- /HEAD -->", headerHTML); |
350 | new StringBuilder buf; |
351 | |
352 | print("Have " + n(conv.msgs, "msg")); |
353 | renderMessages(buf, conv.msgs); |
354 | |
355 | S styles = loadSnippet_simpleCache(#1010888); |
356 | styles = (S) callF_keepOnNull(stylesPost, styles); |
357 | html = html.replace("#BOTFONT#", botFont); |
358 | html = html.replace("#RELATIVESTYLES#", styles); |
359 | html = html.replace("#N#", str(conv.allCount())); |
360 | html = html.replace("#VISITORS#", str(visitorsToday)); |
361 | html = html.replace("#INCREMENTALURL#", relativeRawBotLink(programID(), "incremental"); |
362 | html = html.replace("#MSGURL#", rawBotLink_rel(programID(), "msg?message=")); |
363 | |
364 | // debug mode |
365 | html = html.replace("var showActions = false;", "var showActions = true;"); |
366 | |
367 | int yw = 150, yh = 100; |
368 | |
369 | // MAKE MORE |
370 | |
371 | S more = |
372 | hdiv([[<script language="JavaScript" type="text/javascript"> |
373 | TrustLogo("https://botcompany.de/images/1101147", "CL1", "none"); |
374 | </script>]], style := "float: right") |
375 | // + hsnippetimg(#1101147, width := 113, height := 59, style := "float: right") |
376 | + h3(hsnippetimg(#1009214, width := 389/2, height := 68/2, title := "BotCompany.de") + " – We make actual AI.") |
377 | + p(b(ahref("http://tinybrain.de/x30.jar", "The Software.")) |
378 | + " (Windows/Linux/Mac OS.) Just double-click to run. " + targetBlank("http://java.com/", "Install Java if needed.") + "<br>" |
379 | + b(ahref("mailto:info@botcompany.de", "Mail to order a custom chat bot.")) + " " |
380 | + "Visitors today: " + span(visitorsToday, id := "visnum") + "." + "<br>" |
381 | + "Feel free to talk to " + targetBlank("http://smartbot.botcompany.de", b("Smart Bot")) + " in this chat!" + " - " + targetBlank("http://tinybrain.de/1010745", "Smart Bot's Source Code.") + " " |
382 | + targetBlank("http://javax.tinybrain.de/", "JavaX.") + " " |
383 | + targetBlank("http://tinybrain.de:8080/getraw.php?id=1012854&contentType=text/html", "Impressum.") + "<br>" |
384 | + htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://web-woody-lab.de/", "German smalltalk bot.") + " " |
385 | + htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://botcompany.de/1008316/raw", "Simple English product bot.")) |
386 | + tag('table, tr( |
387 | td(youtubeEmbed("aPQ6Mfa6bEc", yw, yh)) |
388 | + td(youtubeEmbed("7XCb89RJCcE", yw, yh), style := "padding-left: 10px") |
389 | + td(youtubeEmbed("AVqqn-lc_Ic", yw, yh), style := "padding-left: 10px") |
390 | + td(youtubeEmbed("aklRsg59NE8", yw, yh), style := "padding-left: 10px") |
391 | )) |
392 | + h3("Bot's Thoughts " + small("(Type " + tt("!word ...") + " to change)")) |
393 | + tag('iframe, "", src := relativeRawBotLink(programID(), "thoughts"), width:= 700, height := 300) |
394 | ; |
395 | |
396 | html = html.replace("$HEADING", htmlencode(heading) + " | " + targetBlank(selfLink("logs"), "Log")); |
397 | html = html.replace("<!-- MSGS HERE -->", str(buf)); |
398 | html = html.replace("<!--MORE STUFF HERE-->", more); |
399 | html = hreplaceTitle(html, heading); |
400 | html = hmobilefix(html); |
401 | for (O f : pagePostProcessors) pcall { html = or((S) callF(f, html), html); } |
402 | ret html; |
403 | } finally { |
404 | _unregisterThread(); |
405 | } |
406 | } |
407 | |
408 | svoid renderMessages(StringBuilder buf, L<Msg> msgs) { |
409 | for (Msg m : msgs) { |
410 | new Matches mm; |
411 | S html; |
412 | if (startsWith(m.text, "[IMAGE] ", mm)) { |
413 | // IMAGE POST |
414 | S url = trim(mm.get(0)); |
415 | html = targetBlank(url, himg(url, width := 200, title := "User-submitted image", style := "display: block; margin-left: auto; margin-right: auto")); |
416 | } else { |
417 | // TEXT POST |
418 | //dynamize_linkParams.set(new O[] { target := "_blank" }); |
419 | //html = dynamize(m.text); |
420 | html = html_linkURLs_targetBlank(htmlEncode_nlToBr_withIndents(m.text)); |
421 | } |
422 | S name = "?"; |
423 | if (m.user != null) |
424 | name = targetBlank("http://ai1.space/1008750/?ip=" + urlencode(m.user.ipAddress), m.user.ipAddress, style := "color: white", title := "User's IP Address") + (nempty(m.user.cookie) ? " / " + targetBlank("http://ai1.space/" + m.user.cookie, m.user.cookie, style := "color: white", title := "User's Cookie") : "") |
425 | + " | " + targetBlank(/*"http://ai1.space/" + m.globalID*/encyclopediaLink(chatLineSymbol + " " + m.nr), |
426 | /*m.globalID*/m.nr, style := "color: white", title := "Message ID"); |
427 | S parse = html_linkURLs_targetBlank(htmlEncode_nlToBr_withIndents(unnull(m.parse))); |
428 | if (webAuthed()) { |
429 | name += " " + targetBlank(relativeBotLink(programID()) + "?inspam=" + m.globalID, "[spam]"); |
430 | if (!m.botMark) { |
431 | name += " " + targetBlank(relativeBotLink(programID()) + "?rateParse=" + m.globalID + "&ok=1", "[gp]"); |
432 | name += " " + targetBlank(relativeBotLink(programID()) + "?rateParse=" + m.globalID + "&ok=0", "[bp]"); |
433 | name += " " + targetBlank(relativeBotLink(programID()) + "?reparse=" + m.globalID, "[rp]"); |
434 | } |
435 | } |
436 | renderMessage(buf, name, formatTime(m.time), html, m.globalID, m.user == null ? null : m.user.cookie, m.botMark, m.nr, parse, m.goodParse); |
437 | } |
438 | |
439 | if (empty(msgs)) ret; |
440 | L<S> buttons = last(msgs).buttons; |
441 | if (nempty(buttons)) |
442 | appendButtons(buf, buttons); |
443 | } |
444 | |
445 | static O msgsToJSON(L<Msg> msgs) { |
446 | ret map(msgs, func(Msg m) { litorderedmap( |
447 | time := m.time, |
448 | text := m.text, |
449 | ip := m.user.ipAddress, |
450 | cookie := m.user.cookie, |
451 | buttons := m.buttons, |
452 | auth := trueOrNull(m.auth), |
453 | botMark := trueOrNull(m.botMark), |
454 | nr := m.nr, |
455 | globalID := m.globalID |
456 | ) }); |
457 | } |
458 | |
459 | svoid renderMessage(StringBuilder buf, S name, S time, S html, S id, S cookie, bool botMark, int nr, S parse, Bool goodParse) { |
460 | ByCookie bc = findConcept(ByCookie, +cookie); |
461 | S imgID = botMark ? botImageID : #1008359; |
462 | if (nempty(cookie) && !botMark /*XXX*/ && bc != null) imgID = or(bc.avatarID, imgID); |
463 | S imgLink = snippetImgLink(imgID); |
464 | //if (nempty(id)) buf.append(hcomment("Msg ID: " + id)); |
465 | |
466 | if (botMark) parse = null; |
467 | if (nempty(parse)) |
468 | parse = [[<p style="background: white; margin: 0; padding: 0; text-align: right; color: ]] + (isTrue(goodParse) ? "green" : isFalse(goodParse) ? "red" : "#888") + [[;"><small>]] + parse + [[</small>]]; |
469 | |
470 | buf.append([[<div class="direct-chat-msg doted-border" id="msg_#ID#"> |
471 | <div class="direct-chat-info clearfix"> |
472 | <span class="direct-chat-name pull-left">$NAME</span> |
473 | </div> |
474 | <img alt="message user image" src="$IMG" class="direct-chat-img"> |
475 | <div class="direct-chat-text" $STYLE> |
476 | <!-- TEXT --><TEXT /><!-- END TEXT --> |
477 | <PARSE /> |
478 | </div> |
479 | <div class="direct-chat-info clearfix"> |
480 | <span class="direct-chat-timestamp pull-right">$TIME</span> |
481 | </div> |
482 | </div> |
483 | ]].replace("$IMG", imgLink) |
484 | .replace("$NAME", name) |
485 | .replace("$TIME", time) |
486 | .replace("#ID#", id) |
487 | .replace("$STYLE", botMark |
488 | //? "style='text-align: right'" |
489 | //? "style='font-style: italic'" |
490 | ? "style='font-family: " + botFont + "; font-weight: " + botFontWeight + "'" |
491 | : "") |
492 | .replace("<PARSE />", unnull(parse)) |
493 | .replace("<TEXT />", html)); |
494 | } |
495 | |
496 | svoid appendButtons(StringBuilder buf, L<S> buttons) { |
497 | S buttonsHtml = lines(map(buttons, func(S text) { |
498 | hsubmit(text, name := "btn") |
499 | })); |
500 | buf.append([[<div class="direct-chat-msg doted-border"> |
501 | <div class="direct-chat-buttons"> |
502 | $BUTTONS |
503 | </div> |
504 | </div> |
505 | ]].replace("$BUTTONS", buttonsHtml); |
506 | } |
507 | |
508 | svoid appendDate(StringBuilder buf, S date) { |
509 | buf.append([[ |
510 | <div class="chat-box-single-line"> |
511 | <abbr class="timestamp">DATE</abbr> |
512 | </div>]].replace("DATE", date)); |
513 | } |
514 | |
515 | sS formatTime(long time) { |
516 | ret formatGMTWithOptionalDate_24(time); |
517 | } |
518 | |
519 | sS formatMsgForLog(Msg m) { |
520 | ret htmlencode(m.nr + " " + formatDateAndTime(m.time)) + " - " + htmlencode(m.user.ipAddress + ": " + m.text); |
521 | } |
522 | |
523 | static Conversation getConv(fS cookie) { |
524 | ret uniq_sync(Conversation, +cookie); |
525 | } |
526 | |
527 | static L<Msg> allMsgs() { |
528 | new L<Msg> l; |
529 | for (Conversation c) { |
530 | for (L<Msg> msgs : cloneList(c.oldDialogs)) addAll(l, msgs); |
531 | addAll(l, c.msgs); |
532 | } |
533 | ret l; |
534 | } |
535 | |
536 | svoid processMsgCommands(Msg msg) { |
537 | new Matches m; |
538 | if (swic(msg.text, "avatar ", m)) { |
539 | S avatarID = fsI(trim($1)); |
540 | BufferedImage img = loadImage2(avatarID); |
541 | if (img.getWidth() <= 400 && img.getHeight() <= 400) |
542 | cset(uniq(ByCookie, cookie := msg.user.cookie), +avatarID); |
543 | else fail("Avatar too big: " + avatarID); |
544 | } |
545 | } |
546 | |
547 | svoid postMessage(Conversation conv, S message, bool botMark, bool authed) { |
548 | message = shorten(message, messageLimit, " [...]"); |
549 | S ip = clientIP(); |
550 | print("Have message from " + ip + " at " + gmtWithSeconds() + " in thread " + currentThread() + ": " + quote(shorten(message, 80))); |
551 | |
552 | if (startsWith(ip, "66.249.65.")) |
553 | if (matchOneOf(message, "web * is invalid", "web * is correct")) |
554 | ret; |
555 | |
556 | // anti hammer mechanism |
557 | S key = ip + " " + botMark; |
558 | if (eq(lastPosted.get(key), message)) { |
559 | print("Skipping same."); |
560 | ret; |
561 | } |
562 | |
563 | lastPosted.put(key, message); |
564 | |
565 | if (postingDisabled) ret; |
566 | |
567 | if (!botMark && !authed && superSimpleSpamTester(message)) { |
568 | print(logStructureWithDate("spam.log", "Ignoring spam message: " + quote(message))); |
569 | ret; |
570 | } |
571 | |
572 | // ADD NOT SPAM MESSAGE |
573 | Msg msg = new Msg(clientIP(), cookieConcept(), message); |
574 | msg.botMark = botMark; |
575 | msg.auth = authed; |
576 | //msg.nr = toInt(getOpt(last(conv.msgs), 'nr))+1; |
577 | if (conv.counter == 0) cset(conv, counter := conv.allCount()); |
578 | cset(conv, counter := conv.counter+1); |
579 | msg.nr = conv.counter; |
580 | parseMsg(msg); |
581 | |
582 | conv.add(msg); |
583 | print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie + " after add"); |
584 | } |
585 | |
586 | svoid parseAll { |
587 | for (Msg m : allMsgs()) { |
588 | m.parse = null; |
589 | parseMsg(m); |
590 | } |
591 | change(); |
592 | } |
593 | |
594 | svoid parseMsg(Msg msg) { |
595 | { |
596 | if (!showParses) ret; |
597 | lock downloadLock(); |
598 | if (parserModule == null) |
599 | parserModule = hotwire(#1012396); |
600 | } |
601 | pcall { |
602 | if (countLines(msg.text) > 1) msg.parse = null; |
603 | else msg.parse = (S) callOpt(parserModule, 'ai_tripelizeAndRenderTriple, msg.text); |
604 | } |
605 | } |
Began life as a copy of #1008998
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1012254 |
Snippet name: | Stefan's Chat / Smart Bot's Chat [Include] |
Eternal ID of this version: | #1012254/76 |
Text MD5: | 1b70f3109781596b1244d164293888fe |
Author: | stefan |
Category: | javax / a.i. |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2018-08-05 16:14:10 |
Source code size: | 20085 bytes / 605 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 718 / 1223 |
Version history: | 75 change(s) |
Referenced in: | [show references] |