Warning: session_start(): open(/var/lib/php/sessions/sess_ofj7eqicf7nb0h3mf9a440gg3p, 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
sclass WorkerChat {
int workerLongPollTick = 200;
int workerLongPollMaxWait = 1000*60;
O html(S uri, SS params, Conversation conv, AuthedDialogID auth) null {
S uri2 = appendSlash(uri);
bool requestAuthed = auth != null;
if (startsWith(uri2, "/workers-admin/")) {
if (!requestAuthed) ret serveAuthForm(params.get('uri));
ret serveWorkersAdmin(uri, params);
}
if (startsWith(uri2, "/worker/")) {
if (!requestAuthed) ret serveAuthForm(params.get('uri));
if (eq(uri2, "/worker/botOnOff/")) {
cset(conv, botOn := eq("true", params.get("on")));
ret "OK";
}
ret serveWorkerPage(auth, conv, uri, params);
}
}
// TODO: handle deletion properly
S serveWorkersAdmin(S uri, SS params) {
S nav = p(ahref(rawBotLink(dbBotID), "Main admin") + " | " + ahref(baseLink + "/workers-admin", "Workers admin"));
if (eq(uri, "/workers-admin/change-image")) {
long id = parseLong(params.get("id"));
Worker worker = getConcept Worker(id);
File f = workerImageFile(id);
S content;
S b64 = params.get("base64");
if (nempty(b64))
saveFile(f, decodeBASE64(b64));
if (worker == null) content = "Worker not found";
else
content =
nav +
hscript([[
function submitIt() {
var file = $('#myUpload')[0].files[0];
var reader = new FileReader();
reader.onloadend = function () {
var b64 = reader.result.replace(/^data:.+;base64,/, '');
$("#base64").val(b64);
console.log("Got base64 data: " + b64.length);
$("#submitForm").submit();
};
reader.readAsDataURL(file);
return false;
}
]]) +
h2("Worker image for: " + worker.renderAsHTML())
+ p(!fileExists(f) ? "No image set" : himgsrc(rawLink("worker-image/" + id)))
+ hpostform(
hhiddenWithIDAndName("base64")
+ "Choose image: " + hfileupload("accept", "image/png,image/jpeg,image/gif", id := "myUpload") + " "
+ hhidden(+id), action := rawLink("/workers-admin/change-image"), id := "submitForm")
+ hbuttonOnClick_returnFalse("Upload", "submitIt()");
ret hhtml(hhead_title("Change worker image")
+ hsansserif()
+ hbody(loadJQuery()
+ content));
}
HCRUD_Concepts data = new HCRUD_Concepts(Worker);
HCRUD crud = new(rawLink("workers-admin"), data) {
S frame(S title, S contents) {
ret hhtml(hhead_title_htmldecode(title) + hbody(
nav + h1(title) + contents));
}
};
crud.postProcessTableRow = (item, rendered) -> {
printStruct(+item);
long id = parseLong(item.get("id"));
File f = workerImageFile(id);
ret mapPlus(rendered, "Image" :=
f == null ? "???" : !fileExists(f) ? "-" : himgsrc(rawLink("worker-image/" + id)));
};
crud.renderCmds = item -> crud.renderCmds_base(item) + " | " + ahref(rawLink("workers-admin/change-image" + hquery(id := item.get("id"))), "Change image...");
crud.tableClass = "responstable";
ret hsansserif() + hcss_responstable() + crud.renderPage(params);
}
S serveWorkerPage(AuthedDialogID auth, Conversation conv, S uri, SS params) {
S cookie = conv.cookie;
if (nempty(params.get('workerLogOut)))
cset(auth, loggedIn := null);
S loginID = params.get('workerLogIn);
if (nempty(loginID))
cset(auth, loggedIn := getConcept Worker(parseLong(loginID)));
Map map = prependEmptyOptionForHSelect(mapToOrderedMap(conceptsSortedByFieldCI(Worker, 'loginName),
w -> pair(w.id, w.loginName)));
if (auth.loggedIn == null)
ret hsansserif() + p("You are not logged in as a worker")
+ hpostform(
"Log in as: " + hselect("workerLogIn", map, conceptID(auth.loggedIn)) + " " + hsubmit("OK"), action := rawLink("worker"));
// We are logged in
if (eq(afterLastSlash(uri), "conversation")) {
if (conv == null) ret "Conversation not found";
// Serve the checkbox & the JavaScript
S onOffURL = rawLink("worker/botOnOff" + hquery(cookie := conv.cookie) + "&on=");
ret
hsansserif() + loadJQuery()
+ hhidden(cookie := conv.cookie) // for other frame
+ p(hcheckbox("botOn", conv.botOn,
onclick := "$.get(" + jsQuote(onOffURL) + "+ this.checked)") + " Bot on")
+ hscriptsrc(rawLink(hquery(workerMode := 1, cookie := conv.cookie)));
}
if (eq(afterLastSlash(uri), "conversations")) {
cset(auth.loggedIn, lastOnline := now());
bool poll = eq("1", params.get("poll")); // poll mode?
if (poll) {
long seenChange = parseLong(params.get("lastChange"));
vmBus_send chatBot_startingWorkerPoll(mc(), conv);
long start = sysNow();
L msgs;
bool first = true;
while (licensed() && sysNow() < start+workerLongPollMaxWait
&& lastConversationChange == seenChange)
sleep(workerLongPollTick);
// poll times out, no events, send update anyway to update time calculations
}
long pingThreshold = now()-activeConversationTimeout();
L convos = sortByCalculatedFieldDesc(c -> c.lastMsgTime(),
conceptsWithFieldGreaterThan Conversation(lastPing := pingThreshold));
print(+lastConversationChange);
S content =
hhiddenWithID(+lastConversationChange) + tag table(
hsimpletableheader("IP", "Country", "Bot status", "Last change", "Last messages")
+ mapToLines(convos, c -> {
L lastMsgs = lastTwo(c.msgs);
S style = c == conv ? "background: #90EE90" : null;
ret tag tr(
//td(ahref(rawLink("worker/conversation" + hquery(cookie := c.cookie)), c.ip, target := "conversation")) +
td(ahref(rawLink("worker" + hquery(cookie := c.cookie)), c.ip, target := "_top")) +
td(getCountry(c)) +
td(c.botOn ? "Bot on" : "Bot off") +
td(renderHowLongAgo(c.lastMsgTime())) +
td(joinWithBR(lambdaMap renderMsgForWorkerChat(lastMsgs))), +style);
}), class := "responstable");
if (poll) ret content;
S incrementalURL = rawLink("worker/conversations?poll=1&lastChange=");
ret hhtml(
hhead(hsansserif() + loadJQuery())
+ hbody(h3(botName)
+ hpostform(
"Logged in as "
+ htmlEncode2(auth.loggedIn.loginName)
+ " (display name: " + htmlEncode2(auth.loggedIn.displayName) + ")"
+ hhidden(workerLogOut := 1) + " " + hsubmit("Log out"),
target := "_top", action := rawLink("worker"))
+ h3("Active conversations")
+ hcss_responstable()
+ hdivWithID("contentArea", content)
+ hscript([[
function poll_start() {
var lastChange = $("#lastConversationChange").val();
if (!lastChange)
setTimeout(poll_start, 1000);
else {
var url = "#INCREMENTALURL#" + lastChange;
console.log("Loading " + url);
$.get(url, function(src) {
if (src.match(/^ERROR/)) console.log(src);
else {
console.log("Loaded " + src.length + " chars");
$("#contentArea").html(src);
}
setTimeout(poll_start, 1000);
}, 'text')
.fail(function() {
console.log("Rescheduling after fail");
setTimeout(poll_start, 1000);
});
}
}
poll_start();
]].replace("#INCREMENTALURL#", incrementalURL)
));
} // end of worker/conversations part
// serve frame set
ret hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]")
+ hframeset_cols("*,*",
tag frame("", name := "conversations", src := rawLink("worker/conversations" + hquery(+cookie))) +
tag frame("", name := "conversation", src := conv == null ? null : rawLink("worker/conversation" + hquery(+cookie)))));
}
S renderMsgForWorkerChat(Msg msg) {
ret (msg.fromWorker != null ? htmlEncode2(msg.fromWorker.displayName) : msg.fromUser ? "User" : "Bot") + ": " + b(htmlEncode2If(shouldHtmlEncodeMsg(msg), msg.text));
}
Cl workersAvailable() {
long timestamp = now()-workerLongPollMaxWait-10000;
ret filter(list(Worker), w -> !w.away && w.lastOnline >= timestamp);
}
bool anyWorkersAvailable() {
ret nempty(workersAvailable());
}
}