+(: uri-download (-> Url Void))
+(define (uri-download u)
+ (define cache-file-path (url->cache-file-path u))
+ (log-debug "uri-download ~v into ~v" u cache-file-path)
+ (match* ((url-scheme u) (url-host u) (url-port u))
+ [(s h p)
+ #:when (and s h)
+ (define ssl? (string=? s "https"))
+ (define-values (status-line headers body-input)
+ ; TODO Timeout. Currently hangs on slow connections.
+ (http-sendrecv
+ h
+ (url->string (struct-copy url u [scheme #f] [host #f]))
+ #:ssl? ssl?
+ #:port (cond [p p] [ssl? 443] [else 80])
+ #:headers (list (format "User-Agent: ~a" user-agent))
+ ))
+ (log-debug "headers: ~v" headers)
+ (log-debug "status-line: ~v" status-line)
+ (define status
+ (string->number (second (string-split (bytes->string/utf-8 status-line)))))
+ (log-debug "status: ~v" status)
+ ; TODO Handle redirects
+ (if (= 200 status)
+ (call-with-output-file cache-file-path
+ (λ (cache-output)
+ (copy-port body-input cache-output))
+ #:exists 'replace)
+ (raise status))]
+ [(_ _ _)
+ (log-error "Invalid URI: ~v" u)]))
+
+(: timeline-print (-> Out-Format (Listof Msg) Void))
+(define (timeline-print out-format timeline)
+ (void (foldl (match-lambda**
+ [((and m (msg _ _ nick _ _)) (cons prev-nick i))
+ (let ([i (if (string=? prev-nick nick) i (+ 1 i))])
+ (msg-print out-format i m)
+ (cons nick i))])
+ (cons "" 0)
+ timeline)))
+
+(: feed->msgs (-> Feed (Listof Msg)))
+(define (feed->msgs f)
+ (match-define (feed nick uri) f)
+ (log-info "Reading feed nick:~a uri:~v" nick uri)
+ (str->msgs nick uri (uri-read-cached uri)))
+
+(: feed-download (-> Feed Void))
+(define (feed-download f)
+ (match-define (feed nick uri) f)
+ (log-info "Downloading feed nick:~a uri:~a" nick (url->string uri))
+ (with-handlers
+ ([exn:fail?
+ (λ (e)
+ (log-error "Network error nick:~a uri:~v exn:~v" nick uri e)
+ #f)]
+ [integer?
+ (λ (status)
+ (log-error "HTTP error nick:~a uri:~a status:~a" nick uri status)
+ #f)])
+ (uri-download uri)))
+
+(: timeline-download (-> Integer (Listof Feed) Void))
+(define (timeline-download num_workers feeds)
+ ; TODO No need for map - can just iter
+ (void (concurrent-filter-map num_workers feed-download feeds)))