!752 static S cookieName = "cookie"; static int cookieDays = 32; static int cookieCounter = 1; static long countingForUnixDay; static new L ips; static new L cookies; static long cookieLess; static new L uaips; static new MultiSet referers; static new ThreadLocal cookieFromUser; static class Uaip { S ua, ip; *() {} *(S *ua, S *ip) {} } static class Data { long day, ips, cookies, cookieLess; new L uaips; *(long *day, long *ips, long *cookies, long *cookieLess) {} *() {} } static PersistentLog stats; p { stats = new PersistentLog("stats.log"); load("cookieCounter"); load("countingForUnixDay"); load("ips"); load("cookies"); load("cookieLess"); load("uaips"); load("referers"); } static synchronized S html(S uri) { if (eq(uri, "/referers")) ret hmobilefix() + h3("Referers!") + htable( renderMapForTable(orderMapByDescendingValue(referers.asMap()), "Referer", "Count", true)); if (eq(uri, "/stats")) { ret htag("p", "IPs in the day: " + l(ips) + ", cookies in the day: " + l(cookies) + ", cookieless today: " + cookieLess + ". Total cookies given out since all time: " + cookieCounter) + htag("pre", fromLines(reversedList(toLines(stats.fileContents())))); } O session = call(getMainBot(), "getSession"); O cookieHandler = call(getMainBot(), "getCookies"); S cookie = cast call(cookieHandler, "read", cookieName); cookieFromUser.set(cookie); // rotate if day changed long day = unixDay(); if (day != countingForUnixDay) { if (countingForUnixDay != 0) { // archive print("Archiving stats for unix day " + countingForUnixDay + "!"); stats.add(today()); } // rotate countingForUnixDay = day; save("countingForUnixDay"); ips.clear(); save("ips"); cookies.clear(); save("cookies"); cookieLess = 0; save("cookieLess"); uaips = new L; save("uaips"); } // save only cookies returned by user if (!cookies.contains(cookie)) { cookies.add(cookie); save("cookies"); } else { ++cookieLess; save("cookieLess"); } // record IP Map headers = cast call(session, "getHeaders"); S remoteAddr = cast headers.get("remote-addr"); S client = cast headers.get("x-forwarded-for"); if (nempty(client)) remoteAddr += "," + client; if (!empty(remoteAddr) && !ips.contains(remoteAddr)) { ips.add(remoteAddr); save("ips"); } // add uaip S ua = cast headers.get("user-agent"); addUAIP(ua, remoteAddr); // referer S referer = cast headers.get("referer"); if (nempty(referer)) { referers.add(referer); save("referers"); // TODO: rotate etc. } boolean isNew = false; if (cookie == null) { isNew = true; //cookie = "Cookie " + (cookieCounter++); cookie = "Cookie " + randomID(10); //save("cookieCounter"); } call(cookieHandler, "set", cookieName, cookie, cookieDays); // hopefully this refreshes the expiration? S s = "Your " + (isNew ? "new " : "recurring ") + "cookie is: " + cookie; s += "
"; Data y = yesterday(); S yesterday = y == null ? "" : " (" + y.cookies + "/" + y.ips + " yesterday)"; s += l(cookies) + " cookies, " + l(ips) + " IPs" + yesterday + " seen today. " + cookieLess + " requests."; ret s; } synchronized answer { if "visitors today" { ret structure(today()); } if "visitors yesterday" { Data data = yesterday(); if (data == null) ret "There was no yesterday."; ret structure(data); } if "visitors last * days" { int i = max(0, l(stats)-parseInt(m.unq(0))+1); new L stats_; for (Map map : objectToMap(concatLists( subList(stats, i), litlist(today()) ))) stats_.add(mapWithoutKey(map, "uaips")); // drop the long stuff ret "```" + structureLines(stats_) + "```"; } } static Data today() { Data d = new Data(countingForUnixDay, l(ips), l(cookies), cookieLess); d.uaips = cloneList(uaips); ret d; } static synchronized Data yesterday() { ret last(stats); } static void addUAIP(S ua, S ip) { for (Uaip x : uaips) if (eq(x.ua, ua) && eq(x.ip, ip)) ret; uaips.add(new Uaip(ua, ip)); } sS cookieFromUser() { ret cookieFromUser.get(); }