[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: anything is del.icio.us
From: |
Mark Plaksin |
Subject: |
Re: anything is del.icio.us |
Date: |
Sun, 02 Sep 2007 12:56:19 -0400 |
User-agent: |
Gnus/5.110007 (No Gnus v0.7) Emacs/23.0.51 (gnu/linux) |
"address@hidden" <address@hidden> writes:
> On Sep 1, 9:15 pm, Mark Plaksin <address@hidden> wrote:
>
>> Here's an anything source for your del.icio.us posts. It displays the
>> posts which contain anything-pattern in any part of your post (URL,
>> tags, and both the short and extended descriptions). The most recent
>> matching posts appear first.
>
> Interesting idea. :) Though it doesn't work. :(
>
> I tried it on Emacs 22.1.1.
>
> There user-emacs-directory is undefined.
Oops. That must be new in Emacs 23. The version below should work with
Emacs 22.
> I set it and then tried again:
...
> Opening TLS connection with `gnutls-cli -p %p %h'...failed
Do you have gnutls-cli or the openssl binary installed? I had similar
errors from the url library when I first started working on this. I
installed gnutls-cli and then it worked.
------------------------------------------------------------------------------
;; You want to specify a username and password with
;; anything-c-delicious-username and anything-c-delicious-username before
;; loading anything-delicious.el. 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
;; TODO:
;; - Better error handling for unexpected status (503, etc) from url-retrieve?
;; - If anything-c-delicious-check-interval is nil, never check for updates
;; via del.icio.us. Instead check to see whether the cache file has changed.
;; - Use/integrate-with/fix delicious-el?
;; - Add some action choices (sometimes you might want to open in emacs-w3m or
;; firefox or copy URL to kill-ring, etc)
(require 'url-http)
(defvar anything-c-delicious-version "0.2")
(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-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
86400
"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, try to read posts in from cache
(if (= (hash-table-count anything-c-delicious-hash) 0)
(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))
(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)
(delayed)
;; Needed?
(volatile)
(requires-pattern . 3))
"Search your Delicious posts.
It searches each part of every post--the URL, the tags and both descriptions.")
- anything is del.icio.us, Mark Plaksin, 2007/09/01
- Re: anything is del.icio.us, address@hidden, 2007/09/01
- Re: anything is del.icio.us,
Mark Plaksin <=
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/02
- Re: anything is del.icio.us, Michaël Cadilhac, 2007/09/03
- Re: anything is del.icio.us, Richard Stallman, 2007/09/03
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/04
- Re: anything is del.icio.us, Richard Stallman, 2007/09/05
- Message not available
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/05
- Message not available
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/03
- Re: anything is del.icio.us, Michaël Cadilhac, 2007/09/04
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/04