static S webChatBotLogsHTML() { 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 })) { LL dialogs = reversed(unnull(conv.oldDialogs)); l.add(webChatBotLogsHTML_formatDialog(str(conv.id + "/" + (l(dialogs)+1)), conv.msgs)); int i = l(dialogs); for (L msgs : dialogs) l.add(webChatBotLogsHTML_formatDialog(conv.id + "/" + (i--), msgs)); } ret h3_htitle("Chat Logs") + ul(l, null, style := "margin-top: 1em"); }); } sS webChatBotLogsHTML_formatDialog(S id, L msgs) { long startTime = collectMinLong(msgs, 'time); long endTime = collectMaxLong(msgs, 'time); new L lc; for (Msg m : msgs) if (m.fromUser) lc.add("U: " + i(htmlencode(m.text))); else lc.add("B: " + htmlencode(m.text)); S time1 = formatDateAndTime(startTime); S time2 = formatDateAndTime(endTime); time2 = shortenEndTime(time2, time1); ret id + " [" + htmlencode(time1) + " - " + htmlencode(time2) + "]" + ul(lc); }