#!/usr/bin/env racket ;; ;; SPDX-License-Identifier: ISC ;; ;; Copyright © 2019 rsiddharth ;; #lang racket/base (require racket/date) (require racket/list) (require racket/port) (require racket/format) (require racket/string) (require net/http-client) (require openssl) (require html-parsing) (require sxml) (require sxml/sxpath) (define HOST "news.ycombinator.com") (define (fmt n w) (~a n #:width w #:align 'right #:pad-string "0")) (define (slug) (let* ((d (current-date))) (string-join `(,(fmt (date-year d) 4) ,(fmt (date-month d) 2) ,(fmt (date-day d) 2)) ""))) (define (fp) "Fetch HN Front Page." (let ((hc (http-conn-open HOST #:ssl? (ssl-make-client-context 'secure) #:port 443))) (define-values (status headers port) (http-conn-sendrecv! hc "/")) (port->string port))) (define (athings) "Get top 20 athings from HN front page." (let ((x (html->xexp (fp))) (s (sxpath "//tr[@class=\"athing\"]"))) (take (s x) 20))) (define (athing:id a) "Return athing id" (let ((id (sxml:attr a 'id))) (if (empty? id) (error "athing:id: Unable to get id") id))) (define (athing:link a) "Return athing link (HREF DESC)" (let* ((s (sxpath "//a[@class=\"storylink\"]")) (l (s a))) (list (sxml:text l) (sxml:attr (car l) 'href)))) (define (athings:latest h athings:sxml) (cond ((empty? athings:sxml) h) (else (let* ((athing (car athings:sxml)) (id (athing:id athing)) (link (athing:link athing))) (athings:latest (hash-set h (string->symbol id) link) (cdr athings:sxml)))))) (define (athings:data-dir) (let ((dir (build-path (find-system-path 'orig-dir) "data"))) (unless (directory-exists? dir) (make-directory dir)) dir)) (define (athings:file) (build-path (athings:data-dir) (slug))) (define (athings:hash f) (cond ((file-exists? f) (call-with-input-file f (λ (in) (read-json in)))) (else (make-immutable-hash))))