From 93a5b18bb1705850a45377d3cc9b7bb644593c88 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 3 Sep 2025 18:22:29 +0200 Subject: [PATCH] Visitor logs Enable showing visitor logs --- admin.php | 6 +- cleanup.php | 26 ++++ data/known-clients.json | 18 ++- data/known-platforms.json | 15 ++- data/rules.json | 11 ++ img/android.svg | 1 + img/bing.svg | 1 + img/brave.svg | 1 + img/chevron.svg | 1 + img/chrome.svg | 1 + img/chromeold.svg | 1 + img/chromium.svg | 144 +++++++++++++++++++++++ img/cogs.svg | 1 + img/ddg.svg | 1 + img/ecosia.svg | 1 + img/firefox.png | Bin 0 -> 3012 bytes img/firefox.svg | 1 + img/google.svg | 1 + img/hmos.svg | 1 + img/huawei.png | Bin 0 -> 2065 bytes img/incognito.svg | 1 + img/info.svg | 1 + img/ios.svg | 1 + img/ip4.svg | 1 + img/ip6.svg | 1 + img/linux.svg | 1 + img/localhost.svg | 1 + img/macos.svg | 1 + img/msedge.svg | 1 + img/msie.svg | 16 +++ img/opera.svg | 1 + img/placeholder.svg | 1 + img/qwant.svg | 53 +++++++++ img/robot.svg | 1 + img/safari.png | Bin 0 -> 3236 bytes img/safari.svg | 1 + img/samsung.svg | 1 + img/tizen.png | Bin 0 -> 1203 bytes img/uc.svg | 92 +++++++++++++++ img/user.svg | 1 + img/win11.svg | 1 + img/winold.png | Bin 0 -> 2683 bytes img/winold.svg | 1 + script.js | 241 +++++++++++++++++++++++++++++++++++--- style.less | 210 +++++++++++++++++++++++++++++---- 45 files changed, 814 insertions(+), 47 deletions(-) create mode 100644 cleanup.php create mode 100644 data/rules.json create mode 100644 img/android.svg create mode 100644 img/bing.svg create mode 100644 img/brave.svg create mode 100644 img/chevron.svg create mode 100644 img/chrome.svg create mode 100644 img/chromeold.svg create mode 100644 img/chromium.svg create mode 100644 img/cogs.svg create mode 100644 img/ddg.svg create mode 100644 img/ecosia.svg create mode 100644 img/firefox.png create mode 100644 img/firefox.svg create mode 100644 img/google.svg create mode 100644 img/hmos.svg create mode 100644 img/huawei.png create mode 100644 img/incognito.svg create mode 100644 img/info.svg create mode 100644 img/ios.svg create mode 100644 img/ip4.svg create mode 100644 img/ip6.svg create mode 100644 img/linux.svg create mode 100644 img/localhost.svg create mode 100644 img/macos.svg create mode 100644 img/msedge.svg create mode 100644 img/msie.svg create mode 100644 img/opera.svg create mode 100644 img/placeholder.svg create mode 100644 img/qwant.svg create mode 100644 img/robot.svg create mode 100644 img/safari.png create mode 100644 img/safari.svg create mode 100644 img/samsung.svg create mode 100644 img/tizen.png create mode 100644 img/uc.svg create mode 100644 img/user.svg create mode 100644 img/win11.svg create mode 100644 img/winold.png create mode 100644 img/winold.svg diff --git a/admin.php b/admin.php index 11c6f1e..9ce6a51 100644 --- a/admin.php +++ b/admin.php @@ -46,15 +46,15 @@ class admin_plugin_botmon extends AdminPlugin { echo ''; // Beta warning message: - echo '
Please note: This plugin is still in the early stages of development and does not (yet) clean up its logs directory.
To avoid taking up too much space on your server, please remove older logs manually!
'; + echo '
Please note: This plugin is still in the early stages of development and does not (yet) clean up its logs directory.
You can clean up the old log files by clicking here, or by adding the cleanup script to your cron jobs.
'; /* Live tab */ echo '
'; echo '

Today

'; echo '
Loading …
'; echo '
'; - echo '
Visitor log'; - echo '
    '; + echo '
    Visitor logs'; + echo '
    '; echo '
    '; echo '
    busy indicatorInitialising …
    '; echo '
    '; diff --git a/cleanup.php b/cleanup.php new file mode 100644 index 0000000..2f68919 --- /dev/null +++ b/cleanup.php @@ -0,0 +1,26 @@ +

    BotMon Cleanup Script

    + diff --git a/data/known-clients.json b/data/known-clients.json index 5b69c86..aa93358 100644 --- a/data/known-clients.json +++ b/data/known-clients.json @@ -3,6 +3,18 @@ "id": "opera", "rx": [ "\\sOpera\\/.*?Version\\/(\\S+)", "Opera\\/(\\S+)" ] }, + {"n": "Samsung Internet", + "id": "samsung", + "rx": [ "\\sSamsungBrowser\\/(\\S+)" ] + }, + {"n": "UC Browser", + "id": "uc", + "rx": [ "\\sUCBrowser\\/(\\d+\\.\\d+)[\\s\\.]"] + }, + {"n": "HuaweiBrowser", + "id": "huawei", + "rx": [ "\\sHuaweiBrowser\\/(\\d+\\.\\d+)[\\s\\.]", "\\/harmony360Browser\\/(\\d+\\.\\d+)[\\s\\.]"] + }, {"n": "Internet Explorer", "id": "msie", "rx": [ "\\sMSIE\\s(\\d+\\.\\d+b?);" ], @@ -16,9 +28,13 @@ "id": "msedge", "rx": [ "\\sEdg\\/(\\S+)", "\\sEdge\\/(\\S+)" ] }, + {"n": "Old Chrome", + "id": "chromeold", + "rx": [ "\\sChrome\\/(\\d\\d\\.\\d+)[\\.\\s;]" ] + }, {"n": "Chrome", "id": "chrome", - "rx": [ "\\sChrome\\/(\\S+)" ] + "rx": [ "\\sChrome\\/(1\\d\\d\\.\\d+)[\\.\\s;]" ] }, {"n": "Safari", "id": "safari", diff --git a/data/known-platforms.json b/data/known-platforms.json index 7231cf3..3148353 100644 --- a/data/known-platforms.json +++ b/data/known-platforms.json @@ -21,7 +21,18 @@ }, {"n": "Vintage Windows", "id": "winold", - "rx": [ "\\(Windows NT (\\d\\.\\d)[;\\s\\)]","Windows (\\d\\.\\d)[;\\s]" ], - "bot": 0.4 + "rx": [ "\\(Windows NT (\\d\\.\\d)[;\\s\\)]","Windows (\\d\\.\\d)[;\\s]" ] + }, + {"n": "Tizen", + "id": "tizen", + "rx": [ "\\sTizen\\s(\\d+\\.\\d+)[;\\s]" ] + }, + {"n": "HarmonyOS", + "id": "hmos", + "rx": [ "\\sHarmonyOS[;\\/\\s](\\d+\\.\\d)?[\\.\\)\\s]*" ] + }, + {"n": "Chromium", + "id": "chromium", + "rx": [ "Chromebook", "\\sCrOS\\s" ] } ] \ No newline at end of file diff --git a/data/rules.json b/data/rules.json new file mode 100644 index 0000000..c4368d8 --- /dev/null +++ b/data/rules.json @@ -0,0 +1,11 @@ +{ + "ip-ranges": [ + {"range": "191.177.0.0", "mask": 16, "bot": 50} + ], + "rules": [ + {"field": "_client", "item": "id", "op": "equals", "value": "chromeold", "bot": 30} + ], + "thresholds": { + "bot": 50 + } +} \ No newline at end of file diff --git a/img/android.svg b/img/android.svg new file mode 100644 index 0000000..4e58649 --- /dev/null +++ b/img/android.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/bing.svg b/img/bing.svg new file mode 100644 index 0000000..f50b683 --- /dev/null +++ b/img/bing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/brave.svg b/img/brave.svg new file mode 100644 index 0000000..8206e81 --- /dev/null +++ b/img/brave.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/chevron.svg b/img/chevron.svg new file mode 100644 index 0000000..5f0fd51 --- /dev/null +++ b/img/chevron.svg @@ -0,0 +1 @@ +chevron \ No newline at end of file diff --git a/img/chrome.svg b/img/chrome.svg new file mode 100644 index 0000000..f8987d7 --- /dev/null +++ b/img/chrome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/chromeold.svg b/img/chromeold.svg new file mode 100644 index 0000000..31a88aa --- /dev/null +++ b/img/chromeold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/chromium.svg b/img/chromium.svg new file mode 100644 index 0000000..9d3bcf0 --- /dev/null +++ b/img/chromium.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/cogs.svg b/img/cogs.svg new file mode 100644 index 0000000..1595449 --- /dev/null +++ b/img/cogs.svg @@ -0,0 +1 @@ +cogs \ No newline at end of file diff --git a/img/ddg.svg b/img/ddg.svg new file mode 100644 index 0000000..5721e8c --- /dev/null +++ b/img/ddg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/ecosia.svg b/img/ecosia.svg new file mode 100644 index 0000000..fc60eed --- /dev/null +++ b/img/ecosia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/firefox.png b/img/firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..d1f0292486dfc1460175906e5e2d619e3912d6fa GIT binary patch literal 3012 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 R7xkZShNqv$={1qc-vI&p!bt!C literal 0 HcmV?d00001 diff --git a/img/firefox.svg b/img/firefox.svg new file mode 100644 index 0000000..4b14151 --- /dev/null +++ b/img/firefox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/google.svg b/img/google.svg new file mode 100644 index 0000000..9004205 --- /dev/null +++ b/img/google.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/hmos.svg b/img/hmos.svg new file mode 100644 index 0000000..2a553c7 --- /dev/null +++ b/img/hmos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/huawei.png b/img/huawei.png new file mode 100644 index 0000000000000000000000000000000000000000..89f3e5acb90395b1c808a8efbd48c702fc3a56a4 GIT binary patch literal 2065 zcmZ8hXH=8f7X6Zth!_#+MKnO7^dhK0H1vVcj3FopVnPYhM+hKIK@dqqItWq%NEHzY z1R<0O2pBpHRdf)+K}9J#(wq!=!S~*uIcx2^_dRRhd(OINtxLt(TOJgW6#@X@AlAwp z&u8g92*UX5eE7L-003R|F*Czq&CC$E;2^S(zZU=~k9ZJ?c&xfIheRS0IYVkHLc!tq zq@-j#@wX9n5Bnv1fZa*V%XM{~mlm0a5W0Xz7doH&!IKd`7@C|Xm90DEvb9k3=k^g) zP#xtH9L9}RLoxMbtazY>iTAflMPaaBafC}tTN%*33NS2au_<82huE1AZ`o5wdMdA>|FpHUT}*xo_wGEE%~fH z9R6f$m&be3%VuNp;@+(#@OZbhe(-o}?!`-JIDFqXz>?>kf78h;p8wGMf37Juw*P&6%gca zpnxn03V`^^nh&}E>AjIEgCIZi5Wdy{fdLUdU*^N-uE5W{FJJG)qf7w!$T$6;|5X$K z13=n7K!ESmKB=n>D*o@SJl@a4fKw>_fKa54Fu#*>thoun$|lCq#h>6Pe&xd;<)~LK zruce=Mq~M9WxcBUgDUlcCO#$+=F!MR@g8$*(l$v^^!|DY_2D|nPFz%CG}g#b-8>qg zl@en!5X|nJFy9hRV`Em>?C|HgMZ@q_xYnZmWq$j9mqsLbN+dJ>cjrtbnb8@zi{V z4pLI{u%{U4`&`an$AuD1tIh5cw!3@Jsl91+$WbZjJ6Ih=twW<}IT*tS!XT+h1qaoD z$q|R`e+Ch_JgsL62yNr-GzIR;;V-ik{oS0E;S5^=zdr6@lCKON9B4Q?tiIG#f$9`t z<<+G2L)~r|EB1cSLfd`skzh0y;*~e=dDAt+FQ+>l_D5$cuO?T?L&!8{Pfl+}>y&i@ zvc^|F4JH6^>?RKQKXQroDj>DnZgk6-`ascZl{)n(# zPwfMOi^JW#NB45FQ({LvY^TNXPOb=TKcVp#f+Kzvl+CCk%F{rFX+kX2*6#LjW^@Xp zGTu~X!^C-otNZMVwc`){j=0{Z(4jw%4WN-DKqhw40wRkTtXlsVbNpdqi^bz zz`^o^Y7Q^w=gDO|uZ%lv)>A&V&3^I<{Qmc(^fGeIc5)Y!8R2-N(%Sv4YaG)P!%n)X zv{=|~e9UA#WrL)VBL-f+mp*ike7vFZf}*Qc^DnTo?)E2~6T5`Yzfr1xjCZ$Tvd1pW z&!>U&VaRI-j@_7X$D&SrUhvW>C-)j4yq(n|b^FS~#TuM+?rb$dZzB8051KDr) zcD5m4yMJD1Kx5%|rLlHa+8>MH(~YuDzrN|N2~ao6zcr>e-=c^}H8@JnsLjIJ8S3p2 zCGv-Rn-#{RHv4DAoS6N25err_-u+ZJv2#x>R6;{lTbYV$;kCIogqucS*Cghg1xwfy;KjnxGeF zxe7`?OUA9e2TrAiT-!vtX)m{K+32koLL_+4aAsw1MkrU5t`&)0U330q;J4A!B=(iD zq}jmKh-QxQ) zT=6@^8g`bfn_!ua$2ir=d^X8R=|bd}4I5mNdK&|8_b9Oc_;DfhsrZ#`!CT8TfgF+b zbQxjw=GQxnu%}zKg1_UxzUpR;Au%!^yq~9-UsY{QGo&@{0G*Q8G3^vOq|yx5v?-ly z-|FMP)$jUs?qzD^Y3IZ&LUI#DFVd)%t}3g>n5fcpn@BM}&&gW${-rM{#GuALxHo9! zWZDpii>nvaIz9KrbwC5S99Fv?-9dTy#O;fvu4SO>*gJu!lY>*z(|&{9rl%uHuFfCf z5mz(%tG*Stbd2f*tu4+S@;@Wfg0gr%@FRY#zjOZvL7&Fz6Q1D8-~BD4x>_UsQquI! p3Uskjl)mk|6+GhnHzxqhJx~!zr_y_R*n8hM*23QWsVVXHe*hX#n&$uj literal 0 HcmV?d00001 diff --git a/img/incognito.svg b/img/incognito.svg new file mode 100644 index 0000000..52eb818 --- /dev/null +++ b/img/incognito.svg @@ -0,0 +1 @@ +incognito \ No newline at end of file diff --git a/img/info.svg b/img/info.svg new file mode 100644 index 0000000..f1daa8f --- /dev/null +++ b/img/info.svg @@ -0,0 +1 @@ +information \ No newline at end of file diff --git a/img/ios.svg b/img/ios.svg new file mode 100644 index 0000000..81eab92 --- /dev/null +++ b/img/ios.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/ip4.svg b/img/ip4.svg new file mode 100644 index 0000000..cd42250 --- /dev/null +++ b/img/ip4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/ip6.svg b/img/ip6.svg new file mode 100644 index 0000000..36f09d1 --- /dev/null +++ b/img/ip6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/linux.svg b/img/linux.svg new file mode 100644 index 0000000..ad51cb7 --- /dev/null +++ b/img/linux.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/localhost.svg b/img/localhost.svg new file mode 100644 index 0000000..3fed84c --- /dev/null +++ b/img/localhost.svg @@ -0,0 +1 @@ +localhost \ No newline at end of file diff --git a/img/macos.svg b/img/macos.svg new file mode 100644 index 0000000..04dc1c8 --- /dev/null +++ b/img/macos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/msedge.svg b/img/msedge.svg new file mode 100644 index 0000000..f56c0f7 --- /dev/null +++ b/img/msedge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/msie.svg b/img/msie.svg new file mode 100644 index 0000000..9c71290 --- /dev/null +++ b/img/msie.svg @@ -0,0 +1,16 @@ + + + + + + + image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/img/opera.svg b/img/opera.svg new file mode 100644 index 0000000..c038666 --- /dev/null +++ b/img/opera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/placeholder.svg b/img/placeholder.svg new file mode 100644 index 0000000..5c162e9 --- /dev/null +++ b/img/placeholder.svg @@ -0,0 +1 @@ +placeholder \ No newline at end of file diff --git a/img/qwant.svg b/img/qwant.svg new file mode 100644 index 0000000..1893307 --- /dev/null +++ b/img/qwant.svg @@ -0,0 +1,53 @@ + + + + + + + + + diff --git a/img/robot.svg b/img/robot.svg new file mode 100644 index 0000000..6070578 --- /dev/null +++ b/img/robot.svg @@ -0,0 +1 @@ +robot \ No newline at end of file diff --git a/img/safari.png b/img/safari.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe7f7ff6ebc9c2f5fd634210117839eed4d7b2c GIT binary patch literal 3236 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 literal 0 HcmV?d00001 diff --git a/img/safari.svg b/img/safari.svg new file mode 100644 index 0000000..8489163 --- /dev/null +++ b/img/safari.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/samsung.svg b/img/samsung.svg new file mode 100644 index 0000000..d3314de --- /dev/null +++ b/img/samsung.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/tizen.png b/img/tizen.png new file mode 100644 index 0000000000000000000000000000000000000000..236e1286309169f1b83b4a37f5b30613e7672706 GIT binary patch literal 1203 zcmV;k1WfyhP)Px#1ZP1_K>z@;j|==^1poj7a8OKCMI?*SC5zDjhtMsJ(y7GdS(?@siO>Lq&tIL_ z+vW5B|Nnlg+W>~p0EN$!x8U^n{DZCBaHQG!`~8Hj+#`$8x6SE+tlR*B&Kru+6N%7N znAOtY@s70LZ=>0TuHBNh;Gw_cl(^x5tlWRA+cuBV0EW+tvfk$I_v!EW*5dNx?Dp*P z`QhsJ{{H{w?e~YU-2j8n9E#B(iqRsA(YMX$*5mT>_WQHS=P8ZS>G1gR_4?Z7^Zfn( zwae%-j?yuX(gldnABxcch|u2W^!oh%0DjHC)9d^F{=3lXyU*#_F(ExwWin87^kJ4?T*~{AQPnOgIh|pY|*1gc`ht ziqWFK)lu-*9k{oUyFugK?7m({`3>$1w{@b&sel+?V?>df5l4T;bpi_sB@&|aO_ zTAS8doYvLi@(GC00D{iy@c8=t{o(5M*W&WX*zH@J)(wcz9*WWF@At~u?mdyy;OO-d ziP0g7(XGek=%2zZ=~4-h|tmB@b~)t^Y{Di^Z71~ z(ww~FG>+0ol+;d_)lZkzO_tR;kkc56(f-y^5dZ)H32;bRa{vGi!vFvd!vV){sAK>D z0nAB6K~yMHV}JohCT12^HYf)fuyb&7aq~bq2*Ak8$IrzjfGNQwD8$9ZC5#~&M0A z9}pPC70eqF%Ec8H9udh9#l7$i|rnpBovUZDnJbFr|4U0MlZ2US&@*VNY4HzeqCaWytM0wqM4 znp+ZD+uA$0I=i}idi(k(Xb4RN2aSlqq{&l&nx;-;(&myW<(?iiVwprW z>o-8fH#%to`I`#4Hmht&;Nsf4Z97!LU_kkoP>}Lmh zz~BJaK_-Z}@u9; + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/user.svg b/img/user.svg new file mode 100644 index 0000000..d2459f5 --- /dev/null +++ b/img/user.svg @@ -0,0 +1 @@ +User Account \ No newline at end of file diff --git a/img/win11.svg b/img/win11.svg new file mode 100644 index 0000000..aaf6a7a --- /dev/null +++ b/img/win11.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/winold.png b/img/winold.png new file mode 100644 index 0000000000000000000000000000000000000000..bac4773fdc6fd61bbc294a2965d777f348b2c787 GIT binary patch literal 2683 zcmai030M=?7M_GHEKxudRH}Iq11?zz0ZAk*LVy5)00BZ#Adn$M$YL^(09FF63ls}aUU#!sMyczM5dhDUU4 zA_PfkM502WASfIN5}AN#Po+|cBr=gq#={nPd72pFDe+?Y0u99^hZiW1mkFhiP$I@? zIC*iB6o`(+!Z_wFIj9uAV;9TcmJbU-)F4EA0*Uy(IV4Q@jJXE+$XuEzfh6)oiFB%f z4?;}AA34JEecDCIlYYvW#rogoBGCtB$RS25EW}$;Kgj8y0CG;66eNa#a!HCT9%Q6~ zVrYR@35_l^wonNsbG?KjP%MYlrDMsIe}R#o0X-$j5?Ls`s6aZ_UhAV#j>Z)61Ym@a z4<$}O; zcM#LllP!}Z2$Nw<9^$hC!(@0m*;AdI@MOYd1h{E5*jJd4=EZ|xD>8}fh$m6;BnOT? znFe1J$0Z~(jYQHiYpvnEz~@1{|7WjR(HIxlgTb2i#yot+001Degm7yv74>iA{5Zp%fg&thDs2UY`BEEf*@oUiLP& z*PFM^tfm-kTU4=o#`hNAN4J#YB*qH6i_M#d%cW|ZrA33|^ThRPRv>4rg^#!X>+55z z&Rt`_UXQ7?IaXU;Z>^y1idZ)k-*9erOY3@@)Nd8VGi+4TEkkqfU1-6SkTXU|UX7Jy zd2Z}0>V->AZf;$yM+s+cxA9SzrIpXix3Z+;7BEf^zPZ!zc;s%eU&`U>kJTpkju;F^ zu@}!d|FD?1b8FKm%5vZ8b)$fHAA=a~_`1TR*aC~!a?TiXo8D_DeQGTV+gCN+xdm75b(x>^hR`utcndUa(!r69dL@1T%LDisYD*=@?-8t1v_Ap4R@EJ9GUQgD2_ zyfnnGaHX~y-)wI`}o z4-v)GUZ?%_=FM#qxtUdu{AZ$G*=GCZ2ey@Ks6lR~L84S7u4L)Jj(B9$G)uFj9K)DV z>uQ#M^HF)^vp-gr)7GdTNCk=06m+TjbwLNB?SyCkNYml@7LgOz@k1wFeL~gSZfz4R zW_BeU&*Yr%{=x~Esr1*EhWBKso{gte#WfjQ=qxNaE5bWpf1+D3Jym%XX<);RNw628 z-Cmxp3g$Bg`hJ1{i!j`Z(6c>0hhEVlbcgM(ne?)nXWzTBI)kuXH(9qG`4lmVRxmQ; zGvY^ajB*>sT;*{PQzY^YHj4Sm3NW*<&KTqmPc(B(eSZnQ|BBWjE@EC)8PczC?`=9&a>(i=CT<5yV zsDhEp%so+611|!Qg9hnf(Y!J!yr%}`=x2Kkuu?tEy4}!1SZDkTr+D5YtBDZ~f{naU z-PLf2QaDSJv7jQWziJEldY-h}@9y|2ne%T?bv)`$Uf=N?IUnKul$+OUdXjvZ(%vJ+ zks=xvCxt$Iyno{;VpEfgTYa!~XYiwltvA2%v!AcZ70I7@c9_ySQ2y?>zNpDz2d&i` z%PKEB*ZQB5i%a9$Y8Zw1AYh>7tiF9u#pbT?FiJx?>Th+>Zfvv(+SlNcVb=@O;(ZXy z)=b4su|4owg~jc!u)CO-onGI66!mKM4ixnuekq@ocjka4m*T$1_`6znU(44WW`@=V nrH1?NyJeZ|>>IuL#=KF-DwAWz_sDS8{AXk`0==qM#Ag2&cufge literal 0 HcmV?d00001 diff --git a/img/winold.svg b/img/winold.svg new file mode 100644 index 0000000..afdb142 --- /dev/null +++ b/img/winold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/script.js b/script.js index a52dfcc..5ced5d2 100644 --- a/script.js +++ b/script.js @@ -1,11 +1,11 @@ /* DokuWiki BotMon Plugin Script file */ -/* 30.08.2025 - 0.1.6 - pre-release */ +/* 03.09.2025 - 0.1.7 - pre-release */ /* Authors: Sascha Leib */ const BotMon = { init: function() { - console.info('BotMon.init()'); + //console.info('BotMon.init()'); // find the plugin basedir: this._baseDir = document.currentScript.src.substring(0, document.currentScript.src.indexOf('/exe/')) @@ -62,6 +62,25 @@ const BotMon = { offset -= 60; } return ( hours > 0 ? sign * hours + ' h' : '') + (offset > 0 ? ` ${offset} min` : ''); + }, + + /* helper function to create a new element with all attributes and text content */ + _makeElement: function(name, atlist = undefined, text = undefined) { + var r = null; + try { + r = document.createElement(name); + if (atlist) { + for (let attr in atlist) { + r.setAttribute(attr, atlist[attr]); + } + } + if (text) { + r.textContent = text.toString(); + } + } catch(e) { + console.error(e); + } + return r; } } }; @@ -130,7 +149,7 @@ BotMon.live = { // event callback, after the client log has been loaded: _onClientLogLoaded: function() { - console.info('BotMon.live.data._onClientLogLoaded()'); + //console.info('BotMon.live.data._onClientLogLoaded()'); // chain the ticks file to load: BotMon.live.data.loadLogFile('tck', BotMon.live.data._onTicksLogLoaded); @@ -139,7 +158,7 @@ BotMon.live = { // event callback, after the tiker log has been loaded: _onTicksLogLoaded: function() { - console.info('BotMon.live.data._onTicksLogLoaded()'); + //console.info('BotMon.live.data._onTicksLogLoaded()'); // analyse the data: BotMon.live.data.analytics.analyseAll(); @@ -150,7 +169,7 @@ BotMon.live = { // display the data: BotMon.live.gui.overview.make(); - console.log(BotMon.live.data.model._visitors); + //console.log(BotMon.live.data.model._visitors); }, @@ -199,7 +218,7 @@ BotMon.live = { // check if it already exists: let visitor = model.findVisitor(dat.id); if (!visitor) { - const bot = BotMon.live.data.bots.match(dat.client); + const bot = BotMon.live.data.bots.match(dat.agent); model._visitors.push(dat); visitor = dat; @@ -209,8 +228,8 @@ BotMon.live = { visitor._pageViews = []; // array of page views visitor._hasReferrer = false; // has at least one referrer visitor._jsClient = false; // visitor has been seen logged by client js as well - visitor._client = bot ?? BotMon.live.data.clients.match(dat.client) ?? null; // client info (browser, bot, etc.) - visitor._platform = BotMon.live.data.platforms.match(dat.client); // platform info + visitor._client = bot ?? BotMon.live.data.clients.match(dat.agent) ?? null; // client info (browser, bot, etc.) + visitor._platform = BotMon.live.data.platforms.match(dat.agent); // platform info // known bots get the bot ID as identifier: if (bot) visitor.id = bot.id; @@ -343,7 +362,7 @@ BotMon.live = { totalPageViews: 0, bots: { known: 0, - likely: 0, + suspected: 0, human: 0, users: 0 } @@ -352,7 +371,7 @@ BotMon.live = { // sort the visits by type: groups: { knownBots: [], - likelyBots: [], + suspectedBots: [], humans: [], users: [] }, @@ -406,8 +425,8 @@ BotMon.live = { // decide based on the score: if (botScore >= 0.5) { - this.data.bots.likely += 1; - this.groups.likelyBots.push(v); + this.data.bots.suspected += 1; + this.groups.suspectedBots.push(v); } else { this.data.bots.human += 1; this.groups.humans.push(v); @@ -496,7 +515,7 @@ BotMon.live = { } }, - // returns bot info if the clientId matches a known bot, null otherwise: + // returns bot info if the user-agent matches a known bot, null otherwise: match: function(cid) { //console.info('BotMon.live.data.clients.match(',cid,')'); @@ -598,15 +617,15 @@ BotMon.live = { switch (type) { case "srv": typeName = "Server"; - columns = ['ts','ip','pg','id','typ','usr','client','ref']; + columns = ['ts','ip','pg','id','typ','usr','agent','ref']; break; case "log": typeName = "Page load"; - columns = ['ts','ip','pg','id','usr','lt','ref','client']; + columns = ['ts','ip','pg','id','usr','lt','ref','agent']; break; case "tck": typeName = "Ticker"; - columns = ['ts','ip','pg','id','client']; + columns = ['ts','ip','pg','id','agent']; break; default: console.warn(`Unknown log type ${type}.`); @@ -671,6 +690,10 @@ BotMon.live = { }, gui: { + init: function() { + // init the lists view: + this.lists.init(); + }, overview: { make: function() { @@ -694,7 +717,7 @@ BotMon.live = {
    Bots vs. Humans
    Known bots:${data.bots.known}
    -
    Likely bots:${data.bots.likely}
    +
    Suspected bots:${data.bots.suspected}
    Probably humans:${data.bots.human}
    Registered users:${data.bots.users}
    @@ -751,8 +774,190 @@ BotMon.live = { if (txt) BotMon.live.gui.status.setText(txt); } } + }, + + lists: { + init: function() { + + const parent = document.getElementById('botmon__today__visitorlists'); + if (parent) { + + for (let i=0; i < 4; i++) { + + // change the id and title by number: + let listTitle = ''; + let listId = ''; + switch (i) { + case 0: + listTitle = "Registered users"; + listId = 'users'; + break; + case 1: + listTitle = "Probably humans"; + listId = 'humans'; + break; + case 2: + listTitle = "Suspected bots"; + listId = 'suspectedBots'; + break; + case 3: + listTitle = "Known bots"; + listId = 'knownBots'; + break; + default: + console.warn('Unknwon list number.'); + } + + const details = BotMon.t._makeElement('details', { + 'data-group': listId, + 'data-loaded': false + }); + details.appendChild(BotMon.t._makeElement('summary', + undefined, + listTitle + )); + details.addEventListener("toggle", this._onDetailsToggle); + + parent.appendChild(details); + + } + } + }, + + _onDetailsToggle: function(e) { + console.info('BotMon.live.gui.lists._onDetailsToggle()'); + + const target = e.target; + + if (target.getAttribute('data-loaded') == 'false') { // only if not loaded yet + target.setAttribute('data-loaded', 'loading'); + + const fillType = target.getAttribute('data-group'); + const fillList = BotMon.live.data.analytics.groups[fillType]; + if (fillList && fillList.length > 0) { + + const ul = BotMon.t._makeElement('ul'); + + fillList.forEach( (it) => { + ul.appendChild(BotMon.live.gui.lists._makeVisitorItem(it, fillType)); + }); + + target.appendChild(ul); + target.setAttribute('data-loaded', 'true'); + } else { + target.setAttribute('data-loaded', 'false'); + } + + } + }, + + _makeVisitorItem: function(data, type) { + + // shortcut for neater code: + const make = BotMon.t._makeElement; + + const li = make('li'); // root list item + const details = make('details'); + const summary = make('summary'); + details.appendChild(summary); + + const span1 = make('span'); /* left-hand group */ + + let typeDescr = "Seen by: " + ( data.typ == 'php' ? "Server-only": "Server + Client"); + span1.appendChild(make('span', { /* Type */ + 'class': 'icon type type_' + data.typ, + 'title': typeDescr + }, data.typ)); + + span1.appendChild(make('span', { /* ID */ + 'class': 'id' + }, data.id)); + + const platformName = (data._platform ? data._platform.n : 'Unknown'); + span1.appendChild(make('span', { /* Platform */ + 'class': 'icon platform platform_' + (data._platform ? data._platform.id : 'unknown'), + 'title': "Platform: " + platformName + }, platformName)); + + const clientName = (data._client ? data._client.n: 'Unknown') + span1.appendChild(make('span', { /* Client */ + 'class': 'icon client client_' + (data._client ? data._client.id : 'unknown'), + 'title': "Client: " + clientName + }, clientName)); + + let ipType = ( data.ip.indexOf(':') >= 0 ? '6' : '4' ); + if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0'; + span1.appendChild(make('span', { /* IP-Address */ + 'class': 'icon ipaddr ip' + ipType, + 'title': "IP-Address: " + data.ip + }, data.ip)); + + summary.appendChild(span1); + const span2 = make('span'); /* right-hand group */ + + span2.appendChild(make('time', { /* Last seen */ + 'data-field': 'last-seen', + 'datetime': (data._lastSeen ? data._lastSeen : 'unknown') + }, (data._lastSeen ? data._lastSeen.getHours() + ':' + data._lastSeen.getMinutes() + ':' + data._lastSeen.getSeconds() : 'Unknown'))); + + summary.appendChild(span2); + + // create expanable section: + + const dl = make('dl', {'class': 'visitor_details'}); + + dl.appendChild(make('dt', {}, "Client:")); /* client */ + dl.appendChild(make('dd', {'class': 'has_icon client_' + (data._client ? data._client.id : 'unknown')}, + clientName + ( data._client.v > 0 ? ' (' + data._client.v + ')' : '' ) )); + + dl.appendChild(make('dt', {}, "Platform:")); /* platform */ + dl.appendChild(make('dd', {'class': 'has_icon platform_' + (data._platform ? data._platform.id : 'unknown')}, + platformName + ( data._platform.v > 0 ? ' (' + data._platform.v + ')' : '' ) )); + + dl.appendChild(make('dt', {}, "IP-Address:")); + dl.appendChild(make('dd', {'class': 'has_icon ip' + ipType}, data.ip)); + + if ((data._lastSeen - data._firstSeen) < 1) { + dl.appendChild(make('dt', {}, "Seen:")); + dl.appendChild(make('dd', {'class': 'seen'}, data._firstSeen.toLocaleString())); + } else { + dl.appendChild(make('dt', {}, "First seen:")); + dl.appendChild(make('dd', {'class': 'firstSeen'}, data._firstSeen.toLocaleString())); + dl.appendChild(make('dt', {}, "Last seen:")); + dl.appendChild(make('dd', {'class': 'lastSeen'}, data._lastSeen.toLocaleString())); + } + + dl.appendChild(make('dt', {}, "User-Agent:")); + dl.appendChild(make('dd', {'class': 'agent' + ipType}, data.agent)); + + dl.appendChild(make('dt', {}, "Visited pages:")); + const pagesDd = make('dd', {'class': 'pages'}); + const pageList = make('ul'); + + data._pageViews.forEach( (page) => { + const pgLi = make('li'); + + let visitTimeStr = "Bounce"; + const visitDuration = page._lastSeen.getTime() - page._firstSeen.getTime(); + if (visitDuration > 0) { + visitTimeStr = Math.floor(visitDuration / 1000) + "s"; + } + + pgLi.appendChild(make('span', {}, page.pg)); + pgLi.appendChild(make('span', {}, page.ref)); + pgLi.appendChild(make('span', {}, visitTimeStr)); + pageList.appendChild(pgLi); + }); + + pagesDd.appendChild(pageList); + dl.appendChild(pagesDd); + + details.appendChild(dl); + + li.appendChild(details); + return li; + } } - } }; diff --git a/style.less b/style.less index e78fad2..a2a6331 100644 --- a/style.less +++ b/style.less @@ -4,8 +4,24 @@ margin: .25rem 0; } + /* grid layout classes (taken from the Ad-Hoc Wrap plugin) */ + .grid-2-columns, + .grid-3-columns { + display: grid; + } + .grid-2-columns { + grid-template-columns: 1fr 1fr; + grid-gap: 0 .5em; + } + .grid-3-columns { + grid-template-columns: 1fr 1fr 1fr; + grid-gap: 0 .33em; + } + + /* the "today" tab: */ #botmon__today { + /* item header */ header { background-color: #F0F0F0; color: #333; @@ -43,47 +59,181 @@ } } + /* Content */ #botmon__today__content { - & > details { + & details { & { margin: 0 0 1pt 0; text-align: left; } summary { & { + display: flex; + justify-content: flex-start; + align-items: center; + column-gap: .25em; font-weight: bold; font-size: 1rem; line-height: 1.5; - padding: .25rem .5rem; - background-color: #F0F0F0; - color: #333; - border: #CCC solid 1px; - display: flex; - justify-content: space-between; margin: 0; + padding: .25em; + color: #333; + cursor: pointer; } &::marker, &::before { - content: ''; + content: none; display: none; } - &::after { - content: '+'; + &::before { + content: ''; display: inline-block; - color: @ini_link; - background-color: transparent; + width: 1.25em; height: 1.25em; + background: transparent url('img/chevron.svg') center no-repeat; + background-size: 1.25em; + transform: rotate(-90deg); + transition-duration: .25s; } } &[open] { - summary::after { - content: '﹘'; + summary::before { + transform: rotate(0deg); } } & > div { padding: .5rem; + border: #CCC solid 1px; + border-top-width: 0; + border-radius: 0 0 .25rem .25rem; } + & details summary { + background-color: transparent; + border: transparent none 0; + } + } + & > details > summary { + background-color: #F0F0F0; + border: #CCC solid 1px; } } + /* visitor lists: */ + #botmon__today__visitorlists { + details ul { + margin: 0; + padding: 0; + list-style: none; + } + details ul > li { + margin: 0 0 0 .75rem; + padding: 0; + color: #000; + } + details ul > li > details { + border: red dotted 1px; + } + details ul > li > details > summary { + display: flex; + justify-content: space-between; + align-items: center; + column-gap: .5em; + font-weight: normal; + font-size: 1rem; + line-height: 1.5; + border: blue dashed 1px; + } + + details ul > li > details > summary > span { + display: flex; + column-gap: .25em; + } + details ul > li > details > summary > span:first-child { + flex-grow: 1; + } + details ul > li > details > summary > span > span { + height: 1.5em; + overflow: hidden; + } + details ul > li > details > summary > span > span::before { + content: ''; + display: inline-block; + width: 1.25em; height: 1em; + text-align: center; + background: transparent url('img/placeholder.svg') center no-repeat; + background-size: 1em; + } + details ul > li > details > summary > span > span.icon { + width: 1.25em; + overflow: hidden; + } + details ul > li > details > summary > span > span[title] { + cursor: help; + } + + dl.visitor_details { + & { + border: green dotted 1px; + display: grid; + grid-template-columns: min-content auto; + } + dt { + grid-column: 1; + white-space: nowrap; + } + dd { + grid-column: 2; + display: inline-block; + background-color: transparent; + } + } + dd.has_icon::before { + content: ''; + display: inline-block; + width: 1.25em; height: 1.25em; + background: transparent url('img/placeholder.svg') center no-repeat; + background-size: 1em; + } + + /* type icons */ + span.type_dw::before, dd.type_dw::before { background-image: url('img/incognito.svg'); } + span.type_php::before, dd.type_php::before { background-image: url('img/cogs.svg'); } + + /* platform icons */ + span.platform_macos::before, dd.platform_macos::before { background-image: url('img/macos.svg'); } + span.platform_win10::before, dd.platform_win10::before { background-image: url('img/win11.svg'); } + span.platform_linux::before, dd.platform_linux::before { background-image: url('img/linux.svg'); } + span.platform_ios::before, dd.platform_ios::before { background-image: url('img/ios.svg'); } + span.platform_android::before, dd.platform_android::before { background-image: url('img/android.svg'); } + span.platform_winold::before, dd.platform_winold::before { background-image: url('img/winold.svg'); } + span.platform_tizen::before, dd.platform_tizen::before { background-image: url('img/tizen.png'); } + span.platform_hmos::before, dd.platform_hmos::before { background-image: url('img/hmos.svg'); } + span.platform_chromium::before, dd.platform_chromium::before { background-image: url('img/chromium.svg'); } + + + /* browser icons */ + span.client_opera::before, dd.client_opera::before { background-image: url('img/opera.svg'); } + span.client_msie::before, dd.client_msie::before { background-image: url('img/msie.svg'); } + span.client_brave::before, dd.client_brave::before { background-image: url('img/brave.svg'); } + span.client_msedge::before, dd.client_msedge::before { background-image: url('img/msedge.svg'); } + span.client_chrome::before, dd.client_chrome::before { background-image: url('img/chrome.svg'); } + span.client_chromeold::before, dd.client_chromeold::before { background-image: url('img/chromeold.svg'); } + span.client_safari::before, dd.client_safari::before { background-image: url('img/safari.svg'); } + span.client_ddg::before, dd.client_ddg::before { background-image: url('img/ddg.svg'); } + span.client_firefox::before, dd.client_firefox::before { background-image: url('img/firefox.svg'); } + span.client_samsung::before, dd.client_samsung::before { background-image: url('img/samsung.svg'); } + span.client_uc::before, dd.client_uc::before { background-image: url('img/uc.svg'); } + span.client_huawei::before, dd.client_huawei::before { background-image: url('img/huawei.png'); } + + /* ip address type */ + span.ip6::before, dd.ip6::before { background-image: url('img/ip6.svg'); } + span.ip4::before, dd.ip4::before { background-image: url('img/ip4.svg'); } + span.ip0::before, dd.ip0::before { background-image: url('img/localhost.svg'); } + + /* user agent */ + span.agent::before { background-image: url('img/info.svg'); } + + } + + /* item footer */ footer { & { display: flex; @@ -135,18 +285,23 @@ color: #adadb3; } dd:nth-child(even) { - background-color: #4E4E50; + background-color: #333337; } } - #botmon__today__content > details summary { - & { - background-color: #0c0c0d; - color: #adadb3; - border-color: #666; + #botmon__today__content > details { + summary { + & { + background-color: #0c0c0d; + color: #adadb3; + border-color: #666; + } + &::after { + color: #76b0fd; + } } - &::after { - color: #76b0fd; + & > div { + border-color: #666; } } @@ -165,4 +320,13 @@ } } } -} \ No newline at end of file +} +/* layout overrides for narrow screens: */ +@media (max-width: 670px) { + #botmon__admin { + .grid-2-columns, + .grid-3-columns { + grid-template-columns: 100%; + } + } +}