123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- (:eval-when (:compile-toplevel :load-toplevel)
- (ql:quickload :chirp))
- (defpackage :coleslaw-twitter
- (:use :cl)
- (:import-from :coleslaw
- :*config*
- :deploy
- :get-updated-files
- :page-url
- :plugin-conf-error)
- (:export #:enable))
- (in-package :coleslaw-twitter)
- (defvar *tweet-format* '(:title "by" :author)
- "Controls what the tweet annoucing the post looks like.")
- (defvar *tweet-format-fn* nil "Function that expects an instance of
- coleslaw:post and returns the tweet content.")
- (defvar *tweet-format-dsl-mapping*
- '((:title . coleslaw::post-title)
- (:author . coleslaw::post-author)))
- (define-condition malformed-tweet-format (error)
- ((item :initarg :item :reader item))
- (:report
- (lambda (condition stream)
- (format stream "Malformed tweet format. Can't proccess: ~A"
- (item condition)))))
- (defun compile-tweet-format (tweet-format)
- (multiple-value-bind
- (fmt-ctrl-str accesors) (%compile-tweet-format tweet-format nil nil)
- (let
- ((fmt-ctrl-str (format nil "~{~A~^ ~}" (reverse fmt-ctrl-str)))
- (accesors (reverse accesors)))
- (lambda (post)
- (apply #'format nil fmt-ctrl-str
- (loop
- :for accesor :in accesors
- :collect (funcall accesor post)))))))
- (defun %compile-tweet-format (tweet-format fmt-ctrl-str accesors)
- "Transform tweet-format into a format control string and a list of values."
- (if (null tweet-format)
- (values fmt-ctrl-str accesors)
- (let ((next (car tweet-format)))
- (cond
- ((keywordp next)
- (if (assoc next *tweet-format-dsl-mapping*)
- (%compile-tweet-format
- (cdr tweet-format)
- (cons "~A" fmt-ctrl-str)
- (cons (cdr (assoc next *tweet-format-dsl-mapping*))
- accesors))
- (error 'malformed-tweet-format :item next)))
- ((stringp next)
- (%compile-tweet-format (cdr tweet-format)
- (cons next fmt-ctrl-str)
- accesors))
- (t (error 'malformed-tweet-format :item next))))))
- (setf *tweet-format-fn* (compile-tweet-format *tweet-format*))
- (defun enable (&key api-key api-secret access-token access-secret tweet-format)
- (if (and api-key api-secret access-token access-secret)
- (setf chirp:*oauth-api-key* api-key
- chirp:*oauth-api-secret* api-secret
- chirp:*oauth-access-token* access-token
- chirp:*oauth-access-secret* access-secret)
- (error 'plugin-conf-error :plugin "twitter"
- :message "Credentials missing."))
- ;; fallback to chirp for credential erros
- (chirp:account/verify-credentials)
-
- (when tweet-format
- (setf *tweet-format* tweet-format)))
- (defmethod deploy :after (staging)
- (declare (ignore staging))
- (loop :for (state file) :in (get-updated-files)
- :when (and (string= "A" state) (string= "post" (pathname-type file)))
- :do (tweet-new-post file)))
- (defun tweet-new-post (file)
- "Retrieve most recent post from in memory DB and publish it."
- (let ((post (coleslaw::find-content-by-path file)))
- (chirp:statuses/update (%format-post 0 post))))
- (defun %format-post (offset post)
- "Guarantee that the tweet content is 140 chars at most. The 117 comes from
- the spaxe needed for a space and the url."
- (let* ((content-prefix (subseq (render-tweet post) 0 (- 117 offset)))
- (content (format nil "~A ~A/~A" content-prefix
- (coleslaw::domain *config*)
- (page-url post)))
- (content-length (chirp:compute-status-length content)))
- (cond
- ((>= 140 content-length) content)
- ((< 140 content-length) (%format-post (1- offset) post)))))
- (defun render-tweet (post)
- "Sans the url, which is a must."
- (funcall *tweet-format-fn* post))
|