Warning: session_start(): open(/var/lib/php/sessions/sess_bkjs8d674qradjmrqc4vdu4nkg, 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;
static int longPollTick = 100;
static int longPollMaxWait = 1000*60;
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, (O) mc()));
}
static void sayAsync(S session, S text) {
lock dbLock();
Conversation conv = getConv(session);
conv.add(new Msg(false, 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 webChatBotLogsHTML();
S message = trim(params.get("btn"));
if (empty(message)) message = trim(params.get("message"));
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);
}
} // locked
if (eq(uri, "/msg")) ret "OK";
if (eq(uri, "/incremental")) {
int a = parseInt(params.get("a")), origA = a;
//int b = parseInt(params.get("b"));
int as = conv.archiveSize();
a -= as;
//b -= as;
long start = sysNow();
L msgs;
bool first = true;
while (licensed() && sysNow() < start+longPollMaxWait) {
msgs = subList(conv.msgs, a);
if (empty(msgs)) {
if (first) {
print("Long poll starting on " + cookie + ", " + origA + "/" + a);
first = false;
}
sleep(longPollTick);
} else {
if (first) print("Long poll ended.");
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#", relativeRawBotLink(programID(), "incremental?a=");
html = html.replace("#MSGURL#", relativeRawBotLink(programID(), "msg?message="));
if (nempty(params.get("debug")))
html = html.replace("var showActions = false;", "var showActions = true;");
html = html.replace("$HEADING", heading);
html = html.replace("", str(buf));
html = hreplaceTitle(html, heading);
html = hmobilefix(html);
ret html;
}
} finally {
_unregisterThread();
}
}
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), htmlEncode2_nlToBr(trim(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)
});
}