coolguylentz

uptimekuma.tcl

Dec 12th, 2025 (edited)
6,692
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TCL 6.70 KB | None | 0 0
  1. ########## CONFIG ##########
  2.  
  3. # Port Eggdrop will listen on for Uptime Kuma
  4. set kuma_webhook_port   8799
  5.  
  6. # Path Uptime Kuma will call: http://host:8799/kuma
  7. set kuma_webhook_path   "/kuma"
  8.  
  9. # Channel to post notifications into
  10. set kuma_webhook_chan   "#status"
  11.  
  12. # Optional header-based secret: X-Kuma-Secret
  13. # If empty, no secret check is performed.
  14. set kuma_webhook_secret ""
  15.  
  16. ########## COLORS ##########
  17. set kuma_color_reset "\017"
  18. set kuma_color_red   "\00304"
  19. set kuma_color_green "\00303"
  20.  
  21. ########## INTERNAL STATE ##########
  22. array set kuma_state       {}
  23. array set kuma_method      {}
  24. array set kuma_path        {}
  25. array set kuma_len         {}
  26. array set kuma_body        {}
  27. array set kuma_secret_hdr  {}
  28.  
  29. ########## LISTENER SETUP ##########
  30.  
  31. if {![info exists kuma_webhook_initialized]} {
  32.     set kuma_webhook_initialized 1
  33.     listen $kuma_webhook_port script kuma:accept
  34.     putlog "Uptime Kuma webhook (JSON): listening on port $kuma_webhook_port, path $kuma_webhook_path"
  35. }
  36.  
  37. proc kuma:accept {idx} {
  38.     global kuma_state kuma_len kuma_body kuma_secret_hdr
  39.     set kuma_state($idx) "request"
  40.     set kuma_len($idx) 0
  41.     set kuma_body($idx) ""
  42.     set kuma_secret_hdr($idx) ""
  43.     control $idx kuma:control
  44.     return 0
  45. }
  46.  
  47. ########## MAIN CONTROL (request / headers / body) ##########
  48.  
  49. proc kuma:control {idx text} {
  50.     global kuma_state kuma_method kuma_path kuma_len kuma_body kuma_secret_hdr
  51.  
  52.     if {[catch {set state $kuma_state($idx)}]} {
  53.         set kuma_state($idx) "request"
  54.         set state "request"
  55.     }
  56.  
  57.     # Empty line outside body usually means connection closed
  58.     if {$text eq "" && $state ne "body"} {
  59.         kuma:cleanup $idx
  60.         return 0
  61.     }
  62.  
  63.     switch -- $state {
  64.         request {
  65.             # First line: "POST /kuma HTTP/1.1"
  66.             if {![regexp {^([A-Z]+) ([^ ]+) HTTP/} $text -> method path]} {
  67.                 kuma:respond $idx 400 "Bad Request"
  68.                 return 0
  69.             }
  70.             set kuma_method($idx) $method
  71.             set kuma_path($idx)   $path
  72.             set kuma_state($idx)  "headers"
  73.         }
  74.  
  75.         headers {
  76.             # Blank line = end of headers
  77.             if {$text eq ""} {
  78.                 set kuma_state($idx) "body"
  79.                 if {$kuma_len($idx) == 0} {
  80.                     kuma:handle $idx
  81.                 }
  82.                 return 0
  83.             }
  84.  
  85.             if {[regexp -nocase {^Content-Length:\s*([0-9]+)} $text -> len]} {
  86.                 set kuma_len($idx) $len
  87.             } elseif {[regexp -nocase {^X-Kuma-Secret:\s*(.+)} $text -> sec]} {
  88.                 set kuma_secret_hdr($idx) [string trim $sec]
  89.             }
  90.         }
  91.  
  92.         body {
  93.             # Append body content line by line
  94.             append kuma_body($idx) $text
  95.  
  96.             if {$kuma_len($idx) == 0 || [string length $kuma_body($idx)] >= $kuma_len($idx)} {
  97.                 kuma:handle $idx
  98.             }
  99.         }
  100.     }
  101.  
  102.     return 0
  103. }
  104.  
  105. ########## REQUEST HANDLING ##########
  106.  
  107. proc kuma:handle {idx} {
  108.     global kuma_method kuma_path kuma_body kuma_secret_hdr
  109.     global kuma_webhook_path kuma_webhook_secret kuma_webhook_chan
  110.     global kuma_color_reset kuma_color_red kuma_color_green
  111.  
  112.     # Must be POST
  113.     if {$kuma_method($idx) ne "POST"} {
  114.         kuma:respond $idx 405 "Method Not Allowed"
  115.         return
  116.     }
  117.  
  118.     # Strip any query string
  119.     set uri $kuma_path($idx)
  120.     set pathOnly $uri
  121.     if {[regexp {^([^?]+)} $uri -> p]} {
  122.         set pathOnly $p
  123.     }
  124.  
  125.     if {$pathOnly ne $kuma_webhook_path} {
  126.         kuma:respond $idx 404 "Not Found"
  127.         return
  128.     }
  129.  
  130.     # Optional secret check
  131.     if {$kuma_webhook_secret ne ""} {
  132.         if {![info exists kuma_secret_hdr($idx)] || $kuma_secret_hdr($idx) ne $kuma_webhook_secret} {
  133.             kuma:respond $idx 403 "Forbidden"
  134.             return
  135.         }
  136.     }
  137.  
  138.     set body ""
  139.     if {[info exists kuma_body($idx)]} {
  140.         set body [string trim $kuma_body($idx)]
  141.     }
  142.  
  143.     # putlog "KUMA raw body: $body"
  144.  
  145.     # Defaults
  146.     set monitor "Unknown Monitor"
  147.     set reason  "No reason provided"
  148.     set status  "UNKNOWN"
  149.  
  150.     # Monitor name: from monitor.name if possible
  151.     if {[regexp {"monitor"\s*:\s*{[^}]*"name"\s*:\s*"([^"]*)"} $body -> mname]} {
  152.        set monitor $mname
  153.    } elseif {[regexp {"name"\s*:\s*"([^"]*)"} $body -> mname2]} {
  154.         set monitor $mname2
  155.     }
  156.  
  157.     # Reason: heartbeat.msg if present, else top-level msg
  158.     if {[regexp {"heartbeat"\s*:\s*{[^}]*"msg"\s*:\s*"([^"]*)"} $body -> hmsg]} {
  159.        set reason $hmsg
  160.    } elseif {[regexp {"msg"\s*:\s*"([^"]*)"} $body -> topmsg]} {
  161.         set reason $topmsg
  162.     }
  163.  
  164.     # Status: from heartbeat.status (0/1) if possible
  165.     if {[regexp {"heartbeat"\s*:\s*{[^}]*"status"\s*:\s*([0-9]+)} $body -> hstatus]} {
  166.         if {$hstatus eq "1"} {
  167.             set status "UP"
  168.         } elseif {$hstatus eq "0"} {
  169.             set status "DOWN"
  170.         }
  171.     } elseif {[regexp {is (up|down)} [string tolower $reason] -> s]} {
  172.         if {$s eq "up"} {
  173.             set status "UP"
  174.         } elseif {$s eq "down"} {
  175.             set status "DOWN"
  176.         }
  177.     }
  178.  
  179.     # Colorised status
  180.     set status_colored $status
  181.     if {$status eq "UP"} {
  182.         set status_colored "${kuma_color_green}UP${kuma_color_reset}"
  183.     } elseif {$status eq "DOWN"} {
  184.         set status_colored "${kuma_color_red}DOWN${kuma_color_reset}"
  185.     }
  186.  
  187.     # Line 1: "<Monitor Name> is (colored)UP/DOWN"
  188.     set line1 "$monitor is $status_colored"
  189.  
  190.     # Line 2: "<Monitor Name> Failed - <reason>" or "Up - <reason>"
  191.     if {$status eq "DOWN"} {
  192.         set line2 "$monitor Failed - $reason"
  193.     } elseif {$status eq "UP"} {
  194.         set line2 "$monitor Up - $reason"
  195.     } else {
  196.         set line2 "$monitor - $reason"
  197.     }
  198.  
  199.     if {$kuma_webhook_chan ne ""} {
  200.         putserv "PRIVMSG $kuma_webhook_chan :$line1"
  201.         putserv "PRIVMSG $kuma_webhook_chan :$line2"
  202.     }
  203.  
  204.     kuma:respond $idx 200 "OK"
  205. }
  206.  
  207. ########## RESPONSE & CLEANUP ##########
  208.  
  209. proc kuma:respond {idx code text} {
  210.     set body $text
  211.     set len  [string length $body]
  212.  
  213.     # CRLF line endings for strict HTTP parsers
  214.     putdcc $idx "HTTP/1.1 $code $text\r"
  215.     putdcc $idx "Content-Type: text/plain\r"
  216.     putdcc $idx "Content-Length: $len\r"
  217.     putdcc $idx "\r"
  218.     putdcc $idx $body
  219.  
  220.     kuma:cleanup $idx
  221. }
  222.  
  223. proc kuma:cleanup {idx} {
  224.     global kuma_state kuma_method kuma_path kuma_len kuma_body kuma_secret_hdr
  225.  
  226.     catch {unset kuma_state($idx)}
  227.     catch {unset kuma_method($idx)}
  228.     catch {unset kuma_path($idx)}
  229.     catch {unset kuma_len($idx)}
  230.     catch {unset kuma_body($idx)}
  231.     catch {unset kuma_secret_hdr($idx)}
  232.  
  233.     catch {killdcc $idx}
  234. }
  235.  
Advertisement