Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

239
LINES

< > BotCompany Repo | #1029876 // WorkerChat, non-static (for DynNewBot2)

JavaX fragment (include)

1  
class WorkerChat {
2  
  int workerLongPollTick = 200;
3  
  int workerLongPollMaxWait = 1000*30;
4  
  long lastWorkerRequested; // timestamp for notification sound
5  
  S mainAdminLink = "/";
6  
7  
  O html(DynNewBot2.Req req) null {
8  
    SS params = req.params;
9  
    S uri = req.uri, uri2 = appendSlash(uri);
10  
    bool requestAuthed = req.auth != null;
11  
    Conversation conv = req.conv;
12  
  
13  
    if (startsWith(uri2, "/worker/")) {
14  
      if (!req.webRequest.isHttps())
15  
        ret subBot_serveRedirect("https://" + req.webRequest.domain() + req.uri + htmlQuery(req.params));
16  
  
17  
      if (!requestAuthed) ret serveAuthForm(params.get('uri));
18  
      
19  
      if (nempty(params.get("turnBotOn")))
20  
        conv.turnBotOn();
21  
      
22  
      ret serveWorkerPage(req);
23  
    }
24  
  }
25  
  
26  
  // auth is tested before we get here
27  
  S serveWorkerPage(DynNewBot2.Req req) {
28  
    AuthedDialogID auth = req.auth;
29  
    Conversation conv = req.conv;
30  
    S uri = req.uri;
31  
    SS params = req.params;
32  
    S cookie = conv.cookie;
33  
    S uri2 = afterLastSlash(uri);
34  
35  
    if (eq(uri2, "availableWorkers"))
36  
      ret "Available workers: " + or2(joinWithComma(map(workersAvailable(), w -> w.renderAsHTML())), "-");
37  
    
38  
    if (nempty(params.get('workerLogOut)))
39  
      cset(auth, loggedIn := null);
40  
      
41  
    if (auth.loggedIn != null && nempty(params.get('workerAvailableBox)))
42  
      if (cset_trueIfChanged(auth.loggedIn, available := nempty(params.get('workerAvailable))))
43  
        noteConversationChange(); // update list of available workers
44  
45  
    if (nempty(params.get("acceptConversation"))) {
46  
      if (conv.worker == null) { // only if not accepted by anyone
47  
        cset(conv, worker := auth.loggedIn);
48  
        conv.turnBotOff();
49  
      }
50  
    }
51  
      
52  
    S loginID = params.get('workerLogIn);
53  
    if (nempty(loginID))
54  
      cset(auth, loggedIn := getConcept Worker(parseLong(loginID)));
55  
  
56  
    Map map = prependEmptyOptionForHSelect(mapToOrderedMap(conceptsSortedByFieldCI(Worker, 'loginName),
57  
      w -> pair(w.id, w.loginName)));
58  
    if (auth.loggedIn == null)
59  
      ret hsansserif() + p("You are not logged in as a worker")
60  
        + hpostform(
61  
          "Log in as: " + hselect("workerLogIn", map, conceptID(auth.loggedIn)) + " " + hsubmit("OK"), action := botMod().baseLink + "/worker");
62  
          
63  
    // We are logged in
64  
    
65  
    if (eq(uri2, "conversation")) {
66  
      if (conv == null) ret "Conversation not found";
67  
      // Serve the checkbox & the JavaScript
68  
      S onOffURL = botMod().baseLink + "/worker/botOnOff" + hquery(+cookie) + "&on=";
69  
      ret
70  
         hsansserif() + loadJQuery()
71  
       + hhidden(+cookie) // for other frame
72  
       + hpostform(
73  
         hhidden(+cookie) +
74  
         p(renderBotStatus(conv))
75  
          + p(conv.botOn
76  
            ? hsubmit("Accept conversation", name := "acceptConversation")
77  
            : hsubmit("Turn bot back on", name := "turnBotOn"))
78  
          , action := botMod().baseLink + "/worker/innerFrameSet", target := "innerFrameSet")
79  
80  
       // include bot
81  
       + hscriptsrc(botMod().baseLink + "/script" + hquery(workerMode := 1, cookie := conv.cookie));
82  
    }
83  
    
84  
    if (eq(uri2, "conversations")) {
85  
      cset(auth.loggedIn, lastOnline := now());
86  
      bool poll = eq("1", params.get("poll")); // poll mode?
87  
      S content = "";
88  
      
89  
      if (poll) {
90  
        long seenChange = parseLong(params.get("lastChange"));
91  
        vmBus_send chatBot_startingWorkerPoll(mc(), conv);
92  
      
93  
        long start = sysNow();
94  
        L msgs;
95  
        bool first = true;
96  
        while (licensed() && sysNow() < start+workerLongPollMaxWait
97  
          && lastConversationChange == seenChange)
98  
          sleep(workerLongPollTick);
99  
          
100  
        printVars_str(+lastWorkerRequested, +seenChange);
101  
        if (lastWorkerRequested > seenChange)
102  
          content = hscript([[
103  
            window.parent.parent.frames[0].sendDesktopNotification("A worker is requested!", { action: function() { window.focus(); } });
104  
            window.parent.parent.frames[0].playWorkerRequestedSound();
105  
          ]]);
106  
          
107  
        // if poll times out, send update anyway to update time calculations
108  
      }
109  
  
110  
      long pingThreshold = now()-activeConversationTimeout();
111  
      L<Conversation> convos = sortByCalculatedFieldDesc(c -> c.lastMsgTime(),
112  
        conceptsWithFieldGreaterThan Conversation(lastPing := pingThreshold));
113  
        
114  
      content +=
115  
          hhiddenWithID(+lastConversationChange) + tag table(
116  
          hsimpletableheader("IP", "Country", "Bot/worker status", "Last change", "Last messages")
117  
          + mapToLines(convos, c -> {
118  
            L<Msg> lastMsgs = lastTwo(c.msgs);
119  
            S style = c == conv ? "background: #90EE90" : null;
120  
            S convLink = botMod().baseLink + "/worker/innerFrameSet" + hquery(cookie := c.cookie);
121  
            ret tag tr(
122  
              td(ahref(convLink, c.ip, target := "innerFrameSet")) +
123  
              td(getCountry(c)) +
124  
              td(renderBotStatus(c)) +
125  
              td(renderHowLongAgo(c.lastMsgTime())) +
126  
              td(ahref(convLink, hparagraphs(lambdaMap renderMsgForWorkerChat(lastMsgs)), target := "innerFrameSet", style := "text-decoration: none")), +style, /*class := "clickable-row", "data-href" := convLink*/);
127  
          }), class := "responstable");
128  
  
129  
      if (poll) ret content;
130  
  
131  
      S incrementalURL = botMod().baseLink + "/worker/conversations?poll=1&lastChange=";
132  
      
133  
      ret hhtml(
134  
          hhead(hsansserif() + loadJQuery()
135  
        + hscript_clickableRows())
136  
        + hbody(h3(botName)
137  
        + hpostform(
138  
            "Logged in as "
139  
          + htmlEncode2(auth.loggedIn.loginName)
140  
          + " (display name: " + htmlEncode2(auth.loggedIn.displayName) + ")"
141  
          + hhidden(workerAvailableBox := 1)
142  
          + " &nbsp; "
143  
          + hcheckboxWithText("workerAvailable", "I am available", auth.loggedIn.available, onclick := "form.submit()")
144  
          + " &nbsp; "
145  
          + hsubmit("Log out", name := "workerLogOut"),
146  
          target := "innerFrameSet", action := botMod().baseLink + "/worker/innerFrameSet")
147  
        + p("Available workers: " + b(or2(joinWithComma(
148  
          map(workersAvailable(), w -> w.displayName)), "none")))
149  
        + h3("Active conversations")
150  
        + hcss_responstable()
151  
        + hdivWithID("contentArea", content)
152  
        + hscript([[
153  
          function poll_start() {
154  
            var lastChange = $("#lastConversationChange").val();
155  
            if (!lastChange)
156  
              setTimeout(poll_start, 1000);
157  
            else {
158  
              var url = "#INCREMENTALURL#" + lastChange;
159  
              console.log("Loading " + url);
160  
              $.get(url, function(src) {
161  
                if (src.match(/^ERROR/)) console.log(src);
162  
                else {
163  
                  console.log("Loaded " + src.length + " chars");
164  
                  $("#contentArea").html(src);
165  
                }
166  
                setTimeout(poll_start, 1000);
167  
              }, 'text')
168  
                .fail(function() {
169  
                  console.log("Rescheduling after fail");
170  
                  setTimeout(poll_start, 1000);
171  
                });
172  
            }
173  
          }
174  
          poll_start();
175  
        ]].replace("#INCREMENTALURL#", incrementalURL)
176  
        ));
177  
    } // end of worker/conversations part
178  
    
179  
    if (eq(uri2, "notificationArea"))
180  
      ret hhtml(
181  
          hhead(hsansserif() + loadJQuery())
182  
        + hbody(hdesktopNotifications()
183  
        + div(small(
184  
            span(hbutton("CLICK HERE to enable notification sounds!"), id := "enableSoundsBtn")
185  
          + " | "
186  
          + span("", id := "notiStatus")), style := "float: right")
187  
        + hscript([[
188  
          function enableSounds() {
189  
            document.removeEventListener('click', enableSounds);
190  
            $("#enableSoundsBtn").html("Notification sounds enabled");
191  
          }
192  
          document.addEventListener('click', enableSounds);
193  
          
194  
          if (window.workerRequestedSound == null) {
195  
            console.log("Loading worker requested sound");
196  
            window.workerRequestedSound = new Audio("https://botcompany.de/files/1400404/worker-requested.mp3");
197  
          }
198  
          
199  
          function playWorkerRequestedSound() {
200  
            console.log("Playing worker requested sound");
201  
            window.workerRequestedSound.play();
202  
          }
203  
          window.playWorkerRequestedSound = playWorkerRequestedSound;
204  
205  
        ]])));
206  
    
207  
    if (eq(uri2, "innerFrameSet"))
208  
      // serve frame set 2
209  
      ret hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]")
210  
        + hframeset_cols("*,*", 
211  
          tag frame("", name := "conversations", src := botMod().baseLink + "/worker/conversations" + hquery(+cookie)) +
212  
          tag frame("", name := "conversation", src := conv == null ? null : botMod().baseLink + "/worker/conversation" + hquery(+cookie))));
213  
          
214  
    // serve frame set 1
215  
    ret hhtml(hhead_title("Worker Chat [" + auth.loggedIn.loginName + "]")
216  
      + hframeset_rows("50,*", 
217  
        tag frame("", name := "notificationArea", src := botMod().baseLink + "/worker/notificationArea") +
218  
        tag frame("", name := "innerFrameSet", src := conv == null ? null : botMod().baseLink + "/worker/innerFrameSet" + hquery(+cookie))));
219  
        
220  
  }
221  
  
222  
  S renderMsgForWorkerChat(Msg msg) {
223  
    ret (msg.fromWorker != null ? htmlEncode2(msg.fromWorker.displayName) : msg.fromUser ? "User" : "Bot") + ": " + b(htmlEncode2If(shouldHtmlEncodeMsg(msg), msg.text));
224  
  }
225  
  
226  
  Cl<Worker> workersAvailable() {
227  
    long timestamp = now()-workerLongPollMaxWait-10000;
228  
    ret filter(list(Worker), w -> w.available && w.lastOnline >= timestamp);
229  
  }
230  
231  
  bool anyWorkersAvailable() {
232  
    ret nempty(workersAvailable());
233  
  }
234  
  
235  
  S renderBotStatus(Conversation conv) {
236  
   ret "Bot is " + b(conv.botOn ? "on" : "off") + "<br>"
237  
     + "Assigned worker: " + b(conv.worker == null ? "none" : conv.worker.displayName);
238  
  }
239  
}

Author comment

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: 133 / 2860
Version history: 13 change(s)
Referenced in: [show references]