(module c3ma () (import scheme) (import (chicken base)) (import (chicken condition)) (import (chicken format)) (import (chicken port)) (import (chicken process-context)) (import (chicken tcp)) (import (chicken time)) (import doctype) (import http-client) (import intarweb) (import openssl) (import spiffy) (import ssax) (import sxpath) (import sxml-transforms) (import uri-common) (client-software '(("Mozilla" "5.0" "compatible; +https://c3ma.brause.cc"))) (define api-url "https://www.ccc-mannheim.de/roomstate/state.xml") (define (read-xml #!optional (in (current-input-port))) (ssax:xml->sxml in '())) (define (state-sxml->state sxml) ;; NOTE: /raum/timestamp updates along with temperature, so not ;; usable to tell how long the room has been open/closed ;; TODO: figure out what the deal with /raum/door is (let ((status ((sxpath "string(/raum/status)") sxml)) (temperature ((sxpath "number(/raum/temperatur/aussen)") sxml))) (cond ((equal? status "CLOSED") (list #f temperature)) ((equal? status "OPEN") (list #t temperature)) (else (list status temperature))))) (define (c3ma-state) (let ((response (condition-case (with-input-from-request api-url #f read-xml) ((exn http) #f) ((exn i/o net) #f)))) (if response (cons #t (state-sxml->state response)) (list #f #f #f)))) (define (status-page reachable? open? temperature) (with-output-to-string (lambda () (SRV:send-reply (pre-post-order `((doctype-html) (html (@ (lang "de")) (head (meta (@ (charset "utf-8"))) (meta (@ (name "viewport") (content ,(string-append "initial-scale=1.0," "width=device-width," "user-scalable=no")))) (meta (@ (name "apple-mobile-web-app-capable") (content "yes"))) (title "Hat der C3MA offen?") (link (@ (href "touch-icon-iphone.png") (rel "apple-touch-icon"))) (link (@ (href "favicon.ico") (rel "icon") (type "image/x-icon"))) (link (@ (href "style.css") (rel "stylesheet") (type "text/css")))) (body (h1 ,(cond ((not reachable?) "¯\\_(ツ)_/¯") ((not (boolean? open?)) open?) (open? "Ja") (else "Nein"))) (h2 ,(format "Außentemperatur: ~a°C" temperature)) (footer (a (@ (href "#") (onclick "window.location.reload()")) "Neuladen") " | " (a (@ (href ,api-url)) "API"))))) (append doctype-rules universal-conversion-rules)))))) (define (handle-request continue) (let* ((request (current-request)) (method (request-method request)) (path (uri-path (request-uri request)))) (if (and (eq? method 'GET) (equal? path '(/ ""))) (with-headers '((cache-control "no-cache")) (lambda () (send-response body: (apply status-page (c3ma-state))))) (continue)))) (define (main #!optional (host "127.0.0.1") (port "8080")) (tcp-connect-timeout 1000) (ssl-handshake-timeout 1000) (trusted-proxies (list host)) (vhost-map `((".*" . ,handle-request))) (root-path "static/") (server-bind-address host) (server-port (string->number port)) (set-buffering-mode! (current-output-port) #:line) (access-log (current-output-port)) (start-server)) (apply main (command-line-arguments)) )