class WorkerChat {
int workerLongPollTick = 200;
int workerLongPollMaxWait = 1000*30;
long lastWorkerRequested; // timestamp for notification sound
S mainAdminLink = "/";
O html(DynNewBot2.Req req) null {
SS params = req.params;
S uri = req.uri, uri2 = appendSlash(uri);
bool requestAuthed = req.auth != null;
Conversation conv = req.conv;
if (startsWith(uri2, "/worker/")) {
if (!req.webRequest.isHttps())
ret subBot_serveRedirect("https://" + req.webRequest.domain() + req.uri + htmlQuery(req.params));
if (!requestAuthed) ret serveAuthForm(params.get('uri));
if (nempty(params.get("turnBotOn")))
conv.turnBotOn();
ret serveWorkerPage(req);
}
}
// auth is tested before we get here
S serveWorkerPage(DynNewBot2.Req req) {
AuthedDialogID auth = req.auth;
Conversation conv = req.conv;
S uri = req.uri;
SS params = req.params;
S cookie = conv.cookie;
S uri2 = afterLastSlash(uri);
if (eq(uri2, "availableWorkers"))
ret "Available workers: " + or2(joinWithComma(map(workersAvailable(), w -> w.renderAsHTML())), "-");
if (nempty(params.get('workerLogOut)))
cset(auth, loggedIn := null);
if (auth.loggedIn != null && nempty(params.get('workerAvailableBox)))
if (cset_trueIfChanged(auth.loggedIn, available := nempty(params.get('workerAvailable))))
noteConversationChange(); // update list of available workers
if (nempty(params.get("acceptConversation"))) {
if (conv.worker == null) { // only if not accepted by anyone
cset(conv, worker := auth.loggedIn);
conv.turnBotOff();
}
}
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 := botMod().baseLink + "/worker");
// We are logged in
if (eq(uri2, "conversation")) {
if (conv == null) ret "Conversation not found";
// Serve the checkbox & the JavaScript
S onOffURL = botMod().baseLink + "/worker/botOnOff" + hquery(+cookie) + "&on=";
ret
hsansserif() + loadJQuery()
+ hhidden(+cookie) // for other frame
+ hpostform(
hhidden(+cookie) +
p(renderBotStatus(conv))
+ p(conv.botOn
? hsubmit("Accept conversation", name := "acceptConversation")
: hsubmit("Turn bot back on", name := "turnBotOn"))
, action := botMod().baseLink + "/worker/innerFrameSet", target := "innerFrameSet")
// include bot
+ hscriptsrc(botMod().baseLink + "/script" + hquery(workerMode := 1, cookie := conv.cookie));
}
if (eq(uri2, "conversations")) {
cset(auth.loggedIn, lastOnline := now());
bool poll = eq("1", params.get("poll")); // poll mode?
S content = "";
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);
printVars_str(+lastWorkerRequested, +seenChange);
if (lastWorkerRequested > seenChange)
content = hscript([[
window.parent.parent.frames[0].sendDesktopNotification("A worker is requested!", { action: function() { window.focus(); } });
window.parent.parent.frames[0].playWorkerRequestedSound();
]]);
// if poll times out, send update anyway to update time calculations
}
long pingThreshold = now()-activeConversationTimeout();
L<Conversation> convos = sortByCalculatedFieldDesc(c -> c.lastMsgTime(),
conceptsWithFieldGreaterThan Conversation(lastPing := pingThreshold));
content +=
hhiddenWithID(+lastConversationChange) + tag table(
hsimpletableheader("IP", "Country", "Bot/worker status", "Last change", "Last messages")
+ mapToLines(convos, c -> {
L<Msg> lastMsgs = lastTwo(c.msgs);
S style = c == conv ? "background: #90EE90" : null;
S convLink = botMod().baseLink + "/worker/innerFrameSet" + hquery(cookie := c.cookie);
ret tag tr(
td(ahref(convLink, c.ip, target := "innerFrameSet")) +
td(getCountry(c)) +
td(renderBotStatus(c)) +
td(renderHowLongAgo(c.lastMsgTime())) +
td(ahref(convLink, hparagraphs(lambdaMap renderMsgForWorkerChat(lastMsgs)), target := "innerFrameSet", style := "text-decoration: none")), +style, /*class := "clickable-row", "data-href" := convLink*/);
}), class := "responstable");
if (poll) ret content;
S incrementalURL = botMod().baseLink + "/worker/conversations?poll=1&lastChange=";
ret hhtml(
hhead(hsansserif() + loadJQuery()
+ hscript_clickableRows())
+ hbody(h3(botName)
+ hpostform(
"Logged in as "
+ htmlEncode2(auth.loggedIn.loginName)
+ " (display name: " + htmlEncode2(auth.loggedIn.displayName) + ")"
+ hhidden(workerAvailableBox := 1)
+ " "
+ hcheckboxWithText("workerAvailable", "I am available", auth.loggedIn.available, onclick := "form.submit()")
+ " "
+ hsubmit("Log out", name := "workerLogOut"),
target := "innerFrameSet", action := botMod().baseLink + "/worker/innerFrameSet")
+ p("Available workers: " + b(or2(joinWithComma(
map(workersAvailable(), w -> w.displayName)), "none")))
+ 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
if (eq(uri2, "notificationArea"))
ret hhtml(
hhead(hsansserif() + loadJQuery())
+ hbody(hdesktopNotifications()
+ div(small(
span(hbutton("CLICK HERE to enable notification sounds!"), id := "enableSoundsBtn")
+ " | "
+ span("", id := "notiStatus")), style := "float: right")
+ hscript([[
function enableSounds() {
document.removeEventListener('click', enableSounds);
$("#enableSoundsBtn").html("Notification sounds enabled");
}
document.addEventListener('click', enableSounds);
if (window.workerRequestedSound == null) {
console.log("Loading worker requested sound");
window.workerRequestedSound = new Audio("https://botcompany.de/files/1400404/worker-requested.mp3");
}
function playWorkerRequestedSound() {
console.log("Playing worker requested sound");
window.workerRequestedSound.play();
}
window.playWorkerRequestedSound = playWorkerRequestedSound;
]])));
if (eq(uri2, "innerFrameSet"))
// serve frame set 2
ret hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]")
+ hframeset_cols("*,*",
tag frame("", name := "conversations", src := botMod().baseLink + "/worker/conversations" + hquery(+cookie)) +
tag frame("", name := "conversation", src := conv == null ? null : botMod().baseLink + "/worker/conversation" + hquery(+cookie))));
// serve frame set 1
ret hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]")
+ hframeset_rows("50,*",
tag frame("", name := "notificationArea", src := botMod().baseLink + "/worker/notificationArea") +
tag frame("", name := "innerFrameSet", src := conv == null ? null : botMod().baseLink + "/worker/innerFrameSet" + 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<Worker> workersAvailable() {
long timestamp = now()-workerLongPollMaxWait-10000;
ret filter(list(Worker), w -> w.available && w.lastOnline >= timestamp);
}
bool anyWorkersAvailable() {
ret nempty(workersAvailable());
}
S renderBotStatus(Conversation conv) {
ret "Bot is " + b(conv.botOn ? "on" : "off") + "<br>"
+ "Assigned worker: " + b(conv.worker == null ? "none" : conv.worker.displayName);
}
}Began life as a copy of #1028434
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1029876 |
| Snippet name: | WorkerChat, non-static (for DynNewBot2) |
| Eternal ID of this version: | #1029876/14 |
| Text MD5: | 7aadd45e473984a614b63241e323e2f5 |
| Author: | stefan |
| Category: | javax / web chat bots |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2020-09-30 21:37:10 |
| Source code size: | 10069 bytes / 239 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 407 / 3155 |
| Version history: | 13 change(s) |
| Referenced in: | #1029877 - DynNewBot2 [LIVE] #1030497 - DynNewBot2 [backup before MsgRenderer] #1030652 - DynNewBot2 [backup before undo states] |