Mark Plaksin <[EMAIL PROTECTED]> writes: > Here's an anything source for your del.icio.us posts.
This version notices if you update the post cache (anything-c-delicious-post-cache) outside of anything-delicious.el. There's a simple shell script in the comments that you can run once a day to fetch your posts. I do want to resolve the problems fetching posts via the web inside of Emacs. New action list: * browse-url * browse-url-firefox * w3m-browse-url * Copy URL to kill ring. Be sure browse-url-new-window-flag and browse-url-firefox-new-window-is-tab are set as you want. I like this: (setq browse-url-new-window-flag t) (setq browse-url-firefox-new-window-is-tab t) This version also fixes a silly bug which made it check for updates once every 60 days instead of once a day :) ------------------------------------------------------------------------------ ;; anything-delicicous.el ;; ;; [EMAIL PROTECTED] ;; You want to specify a username and password with ;; anything-c-delicious-username and anything-c-delicious-password before ;; loading anything-delicious.el. This is because there seems to be a bug ;; in url-retrieve-synchronously which messes things up when "401 auth ;; required" is returned. See ;; http://comments.gmane.org/gmane.emacs.devel/77533 ;; If url-retrieve-synchronously is causing your grief, run something like this ;; out of cron once a day and anything-delicious will notice your new posts: ;; ;; #!/bin/sh ;; ;; # This gets auth from ~/.netrc. Sample .netrc contents: ;; # machine api.del.icio.us login YOURUSERNAME password YOURPASSWODR ;; ;; dir=~/src/backup/delicious ;; out="$dir/backup.`/bin/date +%Y-%m-%d`.xml.gz" ;; curl -n -s https://api.del.icio.us/v1/posts/all | gzip > $out ;; gunzip -dc $out > ~/.emacs.d/anything-delicious-posts ;; TODO: ;; - Better error handling for unexpected status (503, etc) from url-retrieve? ;; - Use/integrate-with/fix delicious-el? (require 'url-http) (defvar anything-c-delicious-version "0.3") (defvar anything-c-delicious-hash (make-hash-table :test 'equal) "Hash table which maps Delicious hashes to full posts.") (defvar anything-c-delicious-tag-hash (make-hash-table :test 'equal) "Hash table which maps Delicious tags to posts. Each tag maps to a list of hashes. Each hash is a key into `anything-c-delicious-tag-hash'.") (defvar anything-c-delicious-username nil "Delicious username.") (defvar anything-c-delicious-password nil "Delicious password.") (defvar anything-c-delicious-api-hostname "api.del.icio.us" "Delicious API server's FQDN.") (defvar anything-c-delicious-api "/v1/" "Path to Delicious API.") (defvar anything-c-delicious-update (concat anything-c-delicious-api "posts/update") "Path to fetch timestamp of most recent Delicious post.") (defvar anything-c-delicious-all-posts (concat anything-c-delicious-api "posts/all?") "Path to fetch all Delicious posts.") (defvar anything-c-delicious-post-cache (if (boundp 'user-emacs-directory) (concat user-emacs-directory "anything-delicious-posts") "~/.anything-delicious-posts") "Path to Delicious post cache.") (defvar anything-c-delicious-post-cache-timestamp (elt (file-attributes anything-c-delicious-post-cache) 5) "Last noted modification time of cache file.") (defvar anything-c-delicious-update-time nil "Time of last update of all Delicious posts in internal Emacs time format.") (defvar anything-c-delicious-sort-function 'anything-c-delicious-sort-recent-first "Function used to sort posts.") (defvar anything-c-delicious-last-check (current-time) "Last time anything-c-delicious checked for new posts.") (defvar anything-c-delicious-check-interval 1440 "Minutes to wait between checks for new Delicious posts. Defaults to one day.") (defvar anything-c-delicious-user-agent (concat "User-Agent: anything-delicious/" anything-c-delicious-version) "User-Agent to send to Delicious. Delicious asks each application to use its own User-Agent") (defvar anything-c-delicious-debug nil "If non-nil, do not delete buffers created by url-retrieve.") ;; stolen from delicioapi.el (defun anything-c-delicious-auth () "Return the authorization string. It is determined using `anything-c-delicious-username' and `anything-c-delicious-password'." (base64-encode-string (format "%s:%s" anything-c-delicious-username anything-c-delicious-password))) ;; stolen from delicioapi.el (defun anything-c-delicious-register-auth () "Register delicious auth information." (let ((auth-info (list (format "%s:443" anything-c-delicious-api-hostname) (cons "del.icio.us API" (anything-c-delicious-auth))))) (add-to-list 'url-http-real-basic-auth-storage auth-info))) ;; Until the bug in (or my cluefullness about) url-retrieve is fixed. (if (and anything-c-delicious-username anything-c-delicious-password) (anything-c-delicious-register-auth)) ;; There must be a function that does this already! The ones I found and tried ;; didn't work. (defun anything-c-delicious-encode-time (string) "Convert STRING to internal Emacs time. Sample STRING: 2007-08-21T14:42:20Z" (apply 'encode-time (mapcar 'string-to-number (cdr (reverse (split-string string "[-T:Z]")))))) ;; Mostly stolen from delicioapi.el. I changed it so it works when the url ;; is a string or an array. It still feels fragile though. (defadvice url-http-user-agent-string (after anything-c-delicious-user-agent activate) "If talking to api.del.icio.us override User-Agent." (if (or (and (stringp url) (string-match "https*://api.del.icio.us" url)) (and (not (stringp url)) (string= (aref url 4) "api.del.icio.us"))) (setq ad-return-value (concat "User-Agent: " anything-c-delicious-user-agent "\r\n")))) (defun anything-c-delicious-maybe-update-posts () "Maybe fetch new Delicious posts. If `anything-c-delicious-check-interval' has passed, posts have not been read into hashes, fetch posts." ;; If the hash is empty or the timestamp on the cache file has changed, ;; try to read posts in from cache. (if (or (= (hash-table-count anything-c-delicious-hash) 0) (and anything-c-delicious-post-cache-timestamp (< (float-time anything-c-delicious-post-cache-timestamp) (float-time (elt (file-attributes anything-c-delicious-post-cache) 5))))) (anything-c-delicious-get-posts t) ;; Otherwise, if the check interval has passed, check for updates (if (not (time-less-p (current-time) (time-add anything-c-delicious-last-check (seconds-to-time (* anything-c-delicious-check-interval 60))))) (save-excursion (setq anything-c-delicious-last-check (current-time)) (let* ((update-url (concat "https://" anything-c-delicious-api-hostname anything-c-delicious-update)) (buffer (progn (sit-for 1) ;; TODO: This is ugly error handling! (condition-case nil (url-retrieve-synchronously update-url) (error nil))))) (if buffer (progn ;; 8/30/2007: Update URL returns this: ;; ?xml version='1.0' standalone='yes'?> ;; <update time="2007-08-29T11:47:52Z" /> (set-buffer buffer) (goto-char (point-min)) (search-forward "<update " nil t) (if (or (not anything-c-delicious-update-time) (time-less-p anything-c-delicious-update-time (anything-c-delicious-encode-time (anything-c-delicious-read-post-field "time")))) (anything-c-delicious-get-posts) ;; If there's no update and the hashes are not built, ;; try to read posts from cache. (if (= (hash-table-count anything-c-delicious-hash) 0) (anything-c-delicious-get-posts t))) (if (not anything-c-delicious-debug) (kill-buffer buffer))))))))) (defun anything-c-delicious-get-posts (&optional fromcache) "Fetch all delicious posts and create tag tables. If FROMCACHE is non-nil, try to read posts from `anything-c-delicious-post-cache'. If that fails, fetch them via the web." (clrhash anything-c-delicious-hash) (clrhash anything-c-delicious-tag-hash) (save-excursion (let* ((update-url (concat "https://" anything-c-delicious-api-hostname anything-c-delicious-all-posts)) (buffer)) ;; If FROMCACHE is non-nil, try to read the posts from the cache. (if fromcache (progn (setq buffer (get-buffer-create " *delicious post cache*")) (set-buffer buffer) (condition-case nil (insert-file-contents anything-c-delicious-post-cache) ;; If reading from cache fails, read from the web and be sure to ;; cache the result (error (setq buffer nil fromcache nil))))) (if (not buffer) (progn (message "Getting Delicious posts via the web. Hang on.") (sit-for 1) ;; TODO: This is ugly error handling! (setq buffer (condition-case nil (url-retrieve-synchronously update-url) (error nil))))) (if buffer (let (href desc extd hash tags time temp) (set-buffer buffer) (goto-char (point-min)) (search-forward "<posts " nil t) (setq anything-c-delicious-update-time (anything-c-delicious-encode-time (anything-c-delicious-read-post-field "update"))) (while (search-forward "<post " nil t) (setq href (anything-c-delicious-read-post-field "href") desc (anything-c-delicious-read-post-field "description") extd (anything-c-delicious-read-post-field "extended") hash (anything-c-delicious-read-post-field "hash") time (anything-c-delicious-encode-time (anything-c-delicious-read-post-field "time")) tags (split-string (anything-c-delicious-read-post-field "tag") " ")) (puthash hash (list tags href desc extd time) anything-c-delicious-hash ) (dolist (tag tags) (puthash tag (cons hash (gethash tag anything-c-delicious-tag-hash)) anything-c-delicious-tag-hash))) (if (not fromcache) (write-file anything-c-delicious-post-cache)) (setq anything-c-delicious-post-cache-timestamp (elt (file-attributes anything-c-delicious-post-cache) 5)) (if (not anything-c-delicious-debug) (kill-buffer buffer))))))) (defun anything-c-delicious-read-post-field (field) "Return the value of FIELD in the current post." (save-excursion (re-search-forward (concat field "=\"\\(.*?\\)\"")) (match-string 1))) ;; How slow is this? It's pretty speedy for me and my ~1300 posts. (defun anything-c-delicious-string-search (string) "Return a list of delicious posts matching STRING. Searches delicious post tags and contents for STRING. Returns a list of posts." (let ((result)) ;; search tags first (maphash '(lambda (tag hash-list) (if (string-match string tag) (dolist (hash hash-list) (if (not (member hash result)) (add-to-list 'result hash))))) anything-c-delicious-tag-hash) ;; then search everything else (maphash '(lambda (key value) (dolist (item (cdr value)) (if (and (and (stringp item) (string-match string item)) (not (member key result))) (add-to-list 'result key)))) anything-c-delicious-hash) (if (functionp anything-c-delicious-sort-function) (funcall anything-c-delicious-sort-function result) result))) (defun anything-c-delicious-sort-recent-first (list) "Sort posts in LIST with most recent posts first." (sort list '(lambda (a b) (time-less-p (elt (gethash b anything-c-delicious-hash) 4) (elt (gethash a anything-c-delicious-hash) 4))))) (defun anything-c-delicious-filtered-candidate-transformer (list source) "Transform a list of delicious posts into (DISPLAY . REAL) pairs." (mapcar (lambda (candidate) ;; candidate is simply a hash (let ((post (gethash candidate anything-c-delicious-hash))) ;; elt 2 is desc, elt 1 is URL `(,(elt post 2) . ,(elt post 1)))) list)) (defvar anything-c-delicious-source '((name . "delicious") (init . anything-c-delicious-maybe-update-posts) (candidates . (lambda () (anything-c-delicious-string-search anything-pattern))) (match . ((lambda (candidate) (string-match anything-pattern (prin1-to-string (gethash candidate anything-c-delicious-hash) t))))) (filtered-candidate-transformer . (lambda (list source) (anything-c-delicious-filtered-candidate-transformer list source))) (action . (("browse-url" . browse-url) ("browse-url-firefox" . browse-url-firefox) ("w3m-browse-url" . w3m-browse-url) ("Copy URL to kill ring" . (lambda (url) (kill-new url))))) (delayed) ;; Needed? (volatile) (requires-pattern . 3)) "Search your Delicious posts. It searches each part of every post--the URL, the tags and both descriptions.") _______________________________________________ gnu-emacs-sources mailing list [email protected] http://lists.gnu.org/mailman/listinfo/gnu-emacs-sources
