Warning: session_start(): open(/var/lib/php/sessions/sess_cs86kol10idqmokgn3nvhri1mj, 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
sO thoughtBot;
sclass Msg {
long time;
bool fromUser;
S text;
L buttons;
*() {}
*(bool *fromUser, S *text) { time = now(); }
}
concept Conversation {
S cookie;
new LL oldDialogs;
new L msgs;
void add(Msg m) {
msgs.add(m);
change();
}
int allCount() { ret lengthLevel2(oldDialogs) + l(msgs); }
int archiveSize() { ret lengthLevel2(oldDialogs); }
}
svoid pWebChatBot {
db();
thoughtBot = runDependent(thoughtBotID);
Class envType = fieldType(thoughtBot, "env");
if (envType != null)
setOpt(thoughtBot, "env", proxy(envType, mc()));
}
synchronized static void sayAsync(S session, S text) {
getConv(session).add(false, text);
}
synchronized html {
registerVisitor();
if (eq(uri, "/logs"))
ret withDBLock(func -> S {
new L l;
for (Conversation conv : sortByCalculatedFieldDesc(list(Conversation), func(Conversation c) { empty(c.msgs) ? c.created : last(c.msgs).time })) {
l.add(formatDialog(str(conv.id), conv.msgs));
int i = 0;
for (L msgs : unnull(conv.oldDialogs))
l.add(formatDialog(conv.id + "/" + (++i), msgs));
}
ret h3_htitle("Chat Logs") + ul(l);
});
fS cookie = cookieSent();
S message = trim(params.get("btn"));
if (empty(message)) message = trim(params.get("message"));
Conversation conv = getConv(cookie);
if (match("clear", message)) {
conv.oldDialogs.add(conv.msgs);
cset(conv, msgs := new L);
conv.change();
callOpt(thoughtBot, "clearSession", conv.cookie);
message = null;
}
call(thoughtBot, "setSession", cookieSent());
if (empty(conv.msgs))
conv.add(new Msg(false, initialMessage()));
if (nempty(message) && !lastUserMessageWas(conv, message))
conv.add(new Msg(true, message));
if (nempty(conv.msgs) && last(conv.msgs).fromUser) {
S reply = "I'm sorry, I don't understand";
L buttons = null;
pcall {
reply = or2(makeReply(last(conv.msgs).text), reply);
buttons = (L) getThreadLocal(thoughtBot, "buttons");
}
Msg msg = new Msg(false, reply);
msg.buttons = buttons;
conv.add(msg);
}
if (eq(uri, "/incremental")) {
int a = parseInt(params.get("a"));
int b = parseInt(params.get("b"));
int as = conv.archiveSize();
a -= as;
b = b == 0 ? l(conv.msgs) : b-as;
L msgs = subList(conv.msgs, a, b);
if (empty(msgs)) ret "";
new StringBuilder buf;
renderMessages(buf, msgs);
ret "\n" + buf;
}
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("$HEADING", heading);
html = html.replace("", str(buf));
html = hreplaceTitle(html, heading);
html = hmobilefix(html);
ret html;
}
svoid renderMessages(StringBuilder buf, L msgs) {
for (Msg m : msgs)
if (m.fromUser || neq(m.text, "-"))
appendMsg(buf, m.fromUser ? "Esteemed Customer" : botName, formatTime(m.time), htmlencode(m.text), !m.fromUser);
L buttons = last(msgs).buttons;
if (nempty(buttons))
appendButtons(buf, 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 initialMessage() {
ret (S) call(thoughtBot, "initialMessage");
}
static bool lastUserMessageWas(Conversation conv, S message) {
Msg m = last(conv.msgs);
ret m != null && m.fromUser && eq(m.text, message);
}
sS makeReply(S message) {
ret callStaticAnswerMethod(thoughtBot, message);
}
sS formatTime(long time) {
ret formatGMT_24(time);
}
sS formatDialog(S id, L msgs) {
new L lc;
for (Msg m : msgs)
lc.add(htmlencode((m.fromUser ? "> " : "< ") + m.text));
ret id + ul(lc);
}
static Conversation getConv(fS cookie) {
ret withDBLock(func -> Conversation {
uniq(Conversation, +cookie))
});
}