Warning: session_start(): open(/var/lib/php/sessions/sess_e88hh69q4n4op3e3v9rd3cdsqc, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
set flag NoNanoHTTPD.
sS templateID = #1011983;
sS heading = "Smart Bot's Chat | BotCompany";
sS roomName = "main"; // internal
static int maxMessages = 40;
static int visitors;
static int messageLimit = stefansChat_messageLimit();
sbool postingDisabled = false;
static SS lastPosted = new ExpiringHashMap(2000); // ip -> message
static volatile bool started;
static int longPollTick = 100;
static int longPollMaxWait = 1000*60;
static new L pagePostProcessors;
concept WeirdIPs {
Set ips = setInConcept(this, new TreeSet);
}
concept User {
S ipAddress, cookie;
}
concept ByCookie {
S cookie;
S avatarID;
}
sclass Msg {
S globalID = aGlobalIDUnlessLoading();
long time;
User user;
S text;
L buttons;
bool botMark, auth;
int nr;
*() {}
*(S ipAddress, S cookie, S *text) {
user = uniq_sync(User, +ipAddress, +cookie);
time = now();
}
}
concept Conversation {
S cookie;
// TODO: use synchro lists
new LL oldDialogs;
new L spam;
new L msgs;
int counter;
void add(Msg m) {
if (empty(oldDialogs)) oldDialogs.add(new L);
if (l(msgs) >= maxMessages)
last(oldDialogs).add(popFirst(msgs));
msgs.add(m);
change();
pcall { processMsgCommands(m); }
}
// TODO
/*void correctSanity() {
if (empty(msgs)) ret;
int nr = last(msgs).nr;
int c = allCount();
while (nr > c) { msgs.add(Msg("null", "", "filler")); ++c; }
while (nr > c)
}*/
void moveToSpam(Msg m, S toID) {
int i = msgs.indexOf(m);
if (i < 0) ret;
while (i < l(msgs) && neq(toID, msgs.get(i).globalID)) {
spam.add(msgs.get(i));
msgs.remove(i);
change();
if (empty(toID)) break;
}
moveMsgsUp();
}
// move msgs from archive back to main dialog
void moveMsgsUp {
int delta = maxMessages-l(msgs);
print("moveMsgsUp delta=" + delta);
if (delta <= 0) ret;
L old = last(oldDialogs);
if (old == null) ret;
L l = takeLast(delta, old);
print("moveMsgsUp l=" + l(l));
if (empty(l)) ret;
msgs.addAll(0, l);
removeLast(old, delta);
change();
}
int allCount() { ret archiveSize() + l(msgs); }
int archiveSize() { ret lengthLevel2(oldDialogs) + l(spam); }
}
p {
loadPage_forcedTimeout = 10000;
dbIndexing(ByCookie, 'cookie);
started = true;
for (Conversation conv)
print("room=" + conv.cookie + ": allCount=" + conv.allCount() + " old=" + l(conv.oldDialogs));
L l = findConcepts(Conversation, cookie := roomName);
if (l(l) > 1) {
print("FIXING");
deleteConcept(smallestByMethod(l, 'allCount));
}
}
html {
_registerThread();
while (!started) sleep(100);
try {
S _vis = registerVisitor();
int visitorsToday = parseFirstInt(_vis);
main.visitors = visitorsToday;
//fS cookie = cookieSent();
if (eq(uri, "/thoughts")) try {
loadPage_extraHeaders.set(litmap("X-Forwarded-For", clientIP()));
ret hrefresh(10) + loadPage(smartBotURL() + "/thoughts"
+ (webAuthed() ? "?_pretendAuthed=1" : ""));
} catch {
ret hrefresh(10) + hGoogleFontOswald() + hfullcenter("Bot loading...");
}
new Matches mm;
Conversation conv;
{
lock dbLock();
if (eq(uri, "/testauth")) ret "Authed: " + yn(webAuthed());
if (swic(uri, "/setSmartBotURL/", mm)) {
if (!webAuthed()) ret "Not authed";
setSmartBotURL(urldecode(mm.rest()));
ret "OK";
}
if (eq(uri, "/stats"))
ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads()));
if (eq(uri, "/spam"))
ret h3AndTitle("Spam Log") + ul(reversedList(map htmlencode(scanLog("spam.log"))));
if (eq(uri, "/logs"))
ret withDBLock(func -> S {
int step = 100, n = toInt(params.get("n"));
L msgs = reversed(allMsgs()); // TODO: optimize
new L l;
int count = l(msgs);
msgs = subList(msgs, n, n+step);
for (Msg m : msgs)
l.add(formatMsgForLog(m) + "
");
ret h3_htitle("Chat Logs")
+ pageNav2("/logs", count, n, step, 'n)
+ p(join(reversed(l)));
});
conv = getConv(roomName);
if (eq(uri, "/sanity")) {
if (conv == null) ret "No conv";
Msg m = last(conv.msgs);
int c = conv.allCount();
if (m == null) ret "Msgs: " + l(conv.msgs) + " vs " + c;
ret m.nr + " vs " + c;
}
S weirdip = params.get("weirdip");
if (nempty(weirdip) && webAuthed()) {
uniq(WeirdIPs).ips.add(weirdip);
ret "OK";
}
S inspam = params.get("inspam");
S to = params.get("to");
if (possibleGlobalID(inspam) && webAuthed()) {
Msg m = findWhere(conv.msgs, globalID := inspam);
if (m == null) ret "Msg not found";
conv.moveToSpam(m, to);
ret "OK, moved to spam";
}
S message = trim(params.get("btn"));
if (empty(message)) message = trim(params.get("message"));
bool botMark = nempty(params.get("botmark"));
//print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie);
if (match("clear", message)) {
print("Clearing.");
conv.oldDialogs.add(conv.msgs);
cset(conv, msgs := new L);
conv.change();
message = null;
}
if (nempty(message) /*&& !lastUserMessageWas(conv, message)*/)
postMessage(conv, message, botMark);
} // synchronized block
if (eq(uri, "/msg")) ret "OK";
if (eq(uri, "/n")) ret str(conv.allCount());
if (eq(uri, "/lastmsg")) ret struct(msgsToJSON(takeLast(1, conv.msgs)));
if (eq(uri, "/msgs-from-to")) {
int as = conv.archiveSize();
int a = parseInt(params.get('a))-as;
int b = parseInt(params.get('b))-as;
ret serveText(struct(msgsToJSON(subList(conv.msgs, a, b))));
}
if (eq(uri, "/archive-from-to")) {
L l = allMsgs();
int a = parseInt(params.get('a))-l(conv.spam);
int b = parseInt(params.get('b))-l(conv.spam);
ret serveText(struct(msgsToJSON(subList(l, a, b))));
}
if (eq(uri, "/incremental")) {
if (uniq(WeirdIPs).ips.contains(clientIP())) {
print("Weird IP.");
sleepSeconds(10);
}
int a = parseIntOpt(params.get("a"));
int vis = parseIntOpt(params.get("v"));
bool json = nempty(params.get('json)); // Funny thing is, JSON isn't even JSON. It's struct()
long start = sysNow();
L msgs;
bool first = true;
while (licensed() && sysNow() < start+longPollMaxWait) {
int as = conv.archiveSize();
msgs = cloneSubList(conv.msgs, a-as);
//int visitors = main.visitors+random(2);
if (empty(msgs) && (vis == 0 || vis == visitors)) {
if (first) {
print("Long poll starting on " + roomName + ", " + a);
first = false;
}
sleep(longPollTick);
} else {
int ac = conv.allCount();
if (first) print("Long poll ended. a=" + a + ", as=" + as + ", msgs=" + l(msgs) + ", ac=" + ac);
if (json) ret struct/*jsonEncode*/(
litorderedmap(n := conv.allCount(),
msgs := msgsToJSON(msgs)));
new StringBuilder buf;
renderMessages(buf, msgs);
ret "\n"
+ (vis == visitors ? "" : "")
+ buf;
}
}
ret "";
}
lock dbLock();
S html = loadSnippet(templateID); // TODO: cache
new StringBuilder buf;
print("Have " + n(conv.msgs, "msg"));
renderMessages(buf, conv.msgs);
html = html.replace("#RELATIVESTYLES#", loadSnippet(#1010888));
html = html.replace("#N#", str(conv.allCount()));
html = html.replace("#VISITORS#", str(visitorsToday));
html = html.replace("#INCREMENTALURL#", relativeRawBotLink(programID(), "incremental");
html = html.replace("#MSGURL#", rawBotLink_rel(programID(), "msg?message="));
// debug mode
html = html.replace("var showActions = false;", "var showActions = true;");
int yw = 150, yh = 100;
// MAKE MORE
S more =
h3("We make custom chat bots.")
+ p(b(ahref("http://tinybrain.de/x30.jar", "The Software."))
+ " (Windows/Linux/Mac OS.) Just double-click to run. " + targetBlank("http://java.com/", "Install Java if needed.") + "
"
+ ahref("mailto:info@ai1.lol", "Mail.") + " "
+ "Visitors today: " + span(visitorsToday, id := "visnum") + "." + "
"
+ "Feel free to talk to " + b("Smart Bot") + " in this chat!" + " - " + targetBlank("http://tinybrain.de/1010745", "Smart Bot's Source Code.") + " "
+ targetBlank("http://javax.tinybrain.de/", "JavaX.") + "
"
+ htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://web-woody-lab.de/", "German smalltalk bot.") + " "
+ htmlencode(unicode_rightPointingTriangle()) + " " + targetBlank("http://ai1.lol/1008316/raw", "Simple English product bot."))
+ tag('table, tr(
td(youtubeEmbed("xeguVLToOwU", yw, yh))
+ td(youtubeEmbed("z51GRYMSeaI", yw, yh), style := "padding-left: 10px")
+ td(youtubeEmbed("jRb7UpwvJV8", yw, yh), style := "padding-left: 10px")
+ td(youtubeEmbed("gOTPwVVyY-4", yw, yh), style := "padding-left: 10px")
))
+ h3("Bot's Thoughts " + small("(Type " + tt("!word ...") + " to change)"))
+ tag('iframe, "", src := relativeRawBotLink(programID(), "thoughts"), width:= 700, height := 300)
;
html = html.replace("$HEADING", htmlencode(heading) + " | " + targetBlank(selfLink("logs"), "Log"));
html = html.replace("", str(buf));
html = html.replace("", more);
html = hreplaceTitle(html, heading);
html = hmobilefix(html);
for (O f : pagePostProcessors) pcall { html = or((S) callF(f, html), html); }
ret html;
} finally {
_unregisterThread();
}
}
svoid renderMessages(StringBuilder buf, L msgs) {
for (Msg m : msgs) {
new Matches mm;
S html;
if (startsWith(m.text, "[IMAGE] ", mm)) {
// IMAGE POST
S url = trim(mm.get(0));
html = targetBlank(url, himg(url, width := 200, title := "User-submitted image", style := "display: block; margin-left: auto; margin-right: auto"));
} else {
// TEXT POST
//dynamize_linkParams.set(new O[] { target := "_blank" });
//html = dynamize(m.text);
html = html_linkURLs_targetBlank(htmlEncode_nlToBr(m.text));
}
S name = "?";
if (m.user != null)
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") : "")
+ " | " + targetBlank(/*"http://ai1.lol/" + m.globalID*/encyclopediaLink("Chat line " + m.nr),
/*m.globalID*/m.nr, style := "color: white", title := "Message ID");
if (webAuthed()) name += " " + targetBlank(botLink(programID()) + "?inspam=" + m.globalID, "[spam]");
renderMessage(buf, name, formatTime(m.time), html, m.globalID, m.user == null ? null : m.user.cookie, m.botMark, m.nr);
}
if (empty(msgs)) ret;
L buttons = last(msgs).buttons;
if (nempty(buttons))
appendButtons(buf, buttons);
}
static O msgsToJSON(L msgs) {
ret map(msgs, func(Msg m) { litorderedmap(
time := m.time,
text := m.text,
ip := m.user.ipAddress,
cookie := m.user.cookie,
buttons := m.buttons,
auth := trueOrNull(m.auth),
botMark := trueOrNull(m.botMark),
nr := m.nr,
globalID := m.globalID
) });
}
svoid renderMessage(StringBuilder buf, S name, S time, S html, S id, S cookie, bool botMark, int nr) {
ByCookie bc = findConcept(ByCookie, +cookie);
S imgID = botMark ? #1008323 : #1008359;
if (nempty(cookie) && !botMark /*XXX*/ && bc != null) imgID = or(bc.avatarID, imgID);
S imgLink = snippetImgLink(imgID);
//if (nempty(id)) buf.append(hcomment("Msg ID: " + id));
buf.append([[
$NAME
$TEXT
$TIME
]].replace("$IMG", imgLink)
.replace("$NAME", name)
.replace("$TIME", time)
.replace("#ID#", id)
.replace("$STYLE", botMark
//? "style='text-align: right'"
//? "style='font-style: italic'"
? "style='font-family: Pacifico'"
: "")
.replace("$TEXT", html));
}
svoid appendButtons(StringBuilder buf, L buttons) {
S buttonsHtml = lines(map(buttons, func(S text) {
hsubmit(text, name := "btn")
}));
buf.append([[
]].replace("$BUTTONS", buttonsHtml);
}
svoid appendDate(StringBuilder buf, S date) {
buf.append([[
]].replace("DATE", date));
}
sS formatTime(long time) {
ret formatGMTWithOptionalDate_24(time);
}
sS formatMsgForLog(Msg m) {
ret htmlencode(m.nr + " " + formatDateAndTime(m.time)) + " - " + htmlencode(m.user.ipAddress + ": " + m.text);
}
static Conversation getConv(fS cookie) {
ret uniq_sync(Conversation, +cookie);
}
static L allMsgs() {
new L l;
for (Conversation c) {
for (L msgs : c.oldDialogs) l.addAll(msgs);
l.addAll(c.msgs);
}
ret l;
}
svoid processMsgCommands(Msg msg) {
new Matches m;
if (swic(msg.text, "avatar ", m)) {
S avatarID = fsI(trim($1));
BufferedImage img = loadImage2(avatarID);
if (img.getWidth() <= 400 && img.getHeight() <= 400)
cset(uniq(ByCookie, cookie := msg.user.cookie), +avatarID);
else fail("Avatar too big: " + avatarID);
}
}
svoid postMessage(Conversation conv, S message, bool botMark) {
message = shorten(message, messageLimit, " [...]");
S ip = clientIP();
print("Have message from " + ip + " at " + gmtWithSeconds() + " in thread " + currentThread() + ": " + quote(shorten(message, 80)));
if (startsWith(ip, "66.249.65."))
if (matchOneOf(message, "web * is invalid", "web * is correct"))
ret;
// anti hammer mechanism
S key = ip + " " + botMark;
if (eq(lastPosted.get(key), message)) {
print("Skipping same.");
ret;
}
lastPosted.put(key, message);
if (postingDisabled) ret;
if (superSimpleSpamTester(message)) {
print(logStructureWithDate("spam.log", "Ignoring spam message: " + quote(message)));
ret;
}
// ADD NOT SPAM MESSAGE
Msg msg = new Msg(clientIP(), cookieConcept(), message);
msg.botMark = botMark;
msg.auth = webAuthed();
//msg.nr = toInt(getOpt(last(conv.msgs), 'nr))+1;
if (conv.counter == 0) cset(conv, counter := conv.allCount());
cset(conv, counter := conv.counter+1);
msg.nr = conv.counter;
conv.add(msg);
print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie + " after add");
}