;;; slashcache.lisp --- Slashdot headline cache. ;; Copyright 2001 by Dave Pearson ;; $Revision: 1.5 $ ;; slashcache.lisp is free software distributed under the terms of the GNU ;; General Public Licence, version 2. For details see the file COPYING. ;; Uses slashdot.lisp (eval-when (compile load eval) (require :slashdot)) (in-package :org.davep.slashdot) ;; Export local symbols. (export '(*HEADLINES-FILE* *MAX-HEADLINES* UPDATE-HEADLINE-CACHE STORY-CACHE LIST-STORIES)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Configuration variables. (defvar *headlines-file* (merge-pathnames (user-homedir-pathname) (make-pathname :name ".slashdot-headlines")) "Name of the file used to hold the headlines.") (defvar *max-headlines* 100 "Maximum number of headlines to hold in the cache.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions for updating the cache. (defun read-cache () "Load the headline cache." (with-open-file (cache *headlines-file* :direction :input :if-does-not-exist nil) (when cache (with-standard-io-syntax (let ((*read-eval*)) (read cache)))))) (defun write-cache (cache) "Write the cache." (let ((cache (if (> (length cache) *max-headlines*) (subseq cache 0 *max-headlines*) cache))) (with-open-file (cache-file *headlines-file* :direction :output :if-exists :supersede) (prin1 cache cache-file)))) (defun story-as-list (story) "Return STORY as a list." (list (title story) (url story) (posting-time story) (author story) (department story) (topic story) (section story) (image story) ;; This last one is for the benefit of emacs (let ((emacs-time (- (posting-time story) 2208988800))) (cons (ash emacs-time -16) (- emacs-time (ash (ash emacs-time -16) 16)))))) (defun list-as-story (list) "Return LIST as a story." (make-instance 'story :title (second list) :url (cllib:url (third list)) :posting-time (fourth list) :author (fifth list) :department (sixth list) :topic (seventh list) :section (eighth list) :image (ninth list))) (defun update-headline-cache (&optional (xml-source *default-slashdot-xml-url*)) "Update the slashdot headline cache." (let ((cache (read-cache))) (write-cache (sort (loop for story in (nreverse (stories (make-instance 'latest-stories :xml-source xml-source))) unless (assoc (story-id story) cache :test #'string-equal) collect (cons (story-id story) (story-as-list story)) into new-cache finally return (append cache new-cache)) (lambda (x y) (> (fourth x) (fourth y))))))) (defun story-cache () "Return the current story cache." (mapcar #'list-as-story (read-cache))) (defmethod cllib:date-formatter ((format (eql :sd-story-list)) se mi ho da mo ye dd dst tz) "Format method for use with CLLIB:DTTM->STRING." (declare (ignore dd dst tz)) (format nil "~4,'0d-~2,'0d-~2,'0d ~2,'0d:~2,'0d:~2,'0d" ye mo da ho mi se)) (defun list-stories (&optional (count 10)) "Output a formatted story list." (loop for n from 1 to count for headline in (story-cache) do (format t "~A - ~A~%" (cllib:dttm->string (posting-time headline) :format :sd-story-list :tz nil) (title headline)))) (provide 'slashcache) ;;; slashcache.lisp ends here.