Warning: session_start(): open(/var/lib/php/sessions/sess_pthtd3a1hfdhesdvkoaetvp276, 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
!759
sbool bottomRight = false;
sS templateID = bottomRight ? #1008787 : /*#1009000*/#1009081;
sS heading = "Stefan's Chat";
sS roomName = "main"; // internal
static int maxMessages = 100;
static int longPollTick = 100;
static int longPollMaxWait = 1000*60;
static new L pagePostProcessors;
concept Extension {
S code;
double priority;
}
concept User {
S ipAddress, cookie;
}
sclass Msg {
S globalID = aGlobalIDUnlessLoading();
long time;
User user;
S text;
L buttons;
*() {}
*(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;
void add(Msg m) {
if (empty(oldDialogs)) oldDialogs.add(new L);
if (l(msgs) >= maxMessages)
last(oldDialogs).add(popFirst(msgs));
msgs.add(m);
change();
}
void moveToSpam(Msg m) {
if (msgs.contains(m)) {
msgs.remove(m);
spam.add(m);
change();
}
}
int allCount() { ret archiveSize() + l(msgs); }
int archiveSize() { ret lengthLevel2(oldDialogs) + l(spam); }
}
p {
db();
for (Extension e : sortByField('priority, list(Extension)))
pcall { evalJava(e.code); }
}
/*synchronized static void sayAsync(S session, S user, S cookie, S text) {
Conversation conv = getConv(session);
conv.add(new Msg(user, cookie, text));
print("sayAsync " + session + ", new size=" + conv.allCount());
}*/
html {
_registerThread();
registerVisitor();
//fS cookie = cookieSent();
try {
Conversation conv;
{
lock dbLock();
if (eq(uri, "/stats"))
ret "Threads: " + ul_htmlEncode(getThreadNames(registeredThreads()));
if (eq(uri, "/logs"))
ret withDBLock(func -> S {
L msgs = sortByFieldDesc(allMsgs(), 'time);
new L l;
for (Msg m : msgs)
l.add(formatMsgForLog(m) + "
");
ret h3_htitle("Chat Logs") + p(join(l));
});
conv = getConv(roomName);
S inspam = params.get("inspam");
if (possibleGlobalID(inspam)) {
Msg m = findWhere(conv.msgs, globalID := inspam);
if (m == null) ret "Msg not found";
conv.moveToSpam(m);
ret "OK, moved to spam";
}
S message = trim(params.get("btn"));
if (empty(message)) message = trim(params.get("message"));
//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)*/) {
conv.add(new Msg(clientIP(), cookieConcept(), message));
print("Have " + l(conv.msgs) + " msgs in conversation " + conv.cookie + " after add");
}
} // synchronized block
if (eq(uri, "/msg")) ret "OK";
if (eq(uri, "/n")) ret str(conv.allCount());
if (eq(uri, "/incremental")) {
int a = parseInt(params.get("a"));
bool json = nempty(params.get('json));
long start = sysNow();
L msgs;
bool first = true;
while (licensed() && sysNow() < start+longPollMaxWait) {
msgs = subList(conv.msgs, a-conv.archiveSize());
if (empty(msgs)) {
if (first) {
print("Long poll starting on " + roomName + ", " + a);
first = false;
}
sleep(longPollTick);
} else {
if (first) print("Long poll ended.");
if (json) ret struct/*jsonEncode*/(
litorderedmap(n := conv.allCount(),
msgs := msgsToJSON(msgs)));
new StringBuilder buf;
renderMessages(buf, msgs);
ret "\n" + buf;
}
}
ret "";
}
lock dbLock();
S html = loadSnippet(templateID); // TODO: cache
new StringBuilder buf;
renderMessages(buf, conv.msgs);
html = html.replace("#N#", str(conv.allCount()));
html = html.replace("#INCREMENTALURL#", rawBotLink(programID(), "incremental?a=");
html = html.replace("#MSGURL#", rawBotLink(programID(), "msg?message="));
S more = p(targetBlank(selfLink("logs"), "Full Chat Logs"))
+ tag('table, tr(
td(youtubeEmbed("0YXhKgbo3co"))
+ td(youtubeEmbed("DQlRJ5S0yII"), style := "padding-left: 10px")) + tr(
td(youtubeEmbed("9ajGg9eRs_A")
+ td(youtubeEmbed("https://youtu.be/Jk8eXDuTH6U"), style := "padding-left: 10px"))
));
html = html.replace("$HEADING", heading);
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)) {
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 {
dynamize_linkParams.set(new O[] { target := "_blank" });
html = dynamize(m.text);
}
appendMsg(buf, m.user == null ? "?"
: targetBlank("http://ai1.lol/1008750/?ip=" + urlencode(m.user.ipAddress), m.user.ipAddress, style := "color: white") + (nempty(m.user.cookie) ? " / " + ahref("http://ai1.lol/" + m.user.cookie, m.user.cookie, style := "color: white") : ""), formatTime(m.time), html, /*!m.fromUser*/false);
}
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) });
}
svoid appendMsg(StringBuilder buf, S name, S time, S text, bool bot) {
S imgLink = snippetImgLink(bot ? #1008323 : #1008359);
buf.append([[
$NAME
$TEXT
$TIME
]].replace("$IMG", imgLink)
.replace("$NAME", name)
.replace("$TIME", time)
.replace("$TEXT", text));
}
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(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) {
l.addAll(c.msgs);
for (L msgs : c.oldDialogs) l.addAll(msgs);
}
ret l;
}