Flexible Lisp Blogware. Fork for personal use. Mirrored from https://github.com/kingcons/coleslaw originally.

twitter.lisp 3.2KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. (:eval-when (:compile-toplevel :load-toplevel)
  2. (ql:quickload :chirp))
  3. (defpackage :coleslaw-twitter
  4. (:use :cl)
  5. (:import-from :coleslaw #:*config*
  6. #:deploy
  7. #:get-updated-files
  8. #:find-content-by-path
  9. #:title-of
  10. #:author-of
  11. #:page-url
  12. #:plugin-conf-error)
  13. (:export #:enable))
  14. (in-package :coleslaw-twitter)
  15. (defvar *tweet-format* '(:title "by" :author)
  16. "Controls what the tweet annoucing the post looks like.")
  17. (defvar *tweet-format-fn* nil "Function that expects an instance of
  18. coleslaw:post and returns the tweet content.")
  19. (defvar *tweet-format-dsl-mapping*
  20. '((:title title-of)
  21. (:author author-of)))
  22. (define-condition malformed-tweet-format (error)
  23. ((item :initarg :item :reader item))
  24. (:report
  25. (lambda (condition stream)
  26. (format stream "Malformed tweet format. Can't proccess: ~A"
  27. (item condition)))))
  28. (defun compile-tweet-format (tweet-format)
  29. (flet ((accessor-for (x)
  30. (rest (assoc x *tweet-format-dsl-mapping*))))
  31. (lambda (post)
  32. (apply #'format nil "~{~A~^ ~}"
  33. (loop for item in *tweet-format*
  34. unless (or (keywordp item) (stringp item))
  35. (error 'malformed-tweet-format :item item)
  36. when (keywordp item)
  37. collect (funcall (accessor-for item) post)
  38. when (stringp item)
  39. collect item)))))
  40. (defun enable (&key api-key api-secret access-token access-secret tweet-format)
  41. (if (and api-key api-secret access-token access-secret)
  42. (setf chirp:*oauth-api-key* api-key
  43. chirp:*oauth-api-secret* api-secret
  44. chirp:*oauth-access-token* access-token
  45. chirp:*oauth-access-secret* access-secret)
  46. (error 'plugin-conf-error :plugin "twitter"
  47. :message "Credentials missing."))
  48. ;; fallback to chirp for credential erros
  49. (chirp:account/verify-credentials)
  50. (when tweet-format
  51. (setf *tweet-format* tweet-format))
  52. (setf *tweet-format-fn* (compile-tweet-format *tweet-format*)))
  53. (defmethod deploy :after (staging)
  54. (declare (ignore staging))
  55. (loop :for (state file) :in (get-updated-files)
  56. :when (and (string= "A" state) (string= "post" (pathname-type file)))
  57. :do (tweet-new-post file)))
  58. (defun tweet-new-post (file)
  59. "Retrieve content matching FILE from in memory DB and publish it."
  60. (let ((post (find-content-by-path file)))
  61. (chirp:statuses/update (%format-post 0 post))))
  62. (defun %format-post (offset post)
  63. "Guarantee that the tweet content is 140 chars at most. The 117 comes from
  64. the spaxe needed for a space and the url."
  65. (let* ((content-prefix (subseq (render-tweet post) 0 (- 117 offset)))
  66. (content (format nil "~A ~A/~A" content-prefix
  67. (coleslaw::domain *config*)
  68. (page-url post)))
  69. (content-length (chirp:compute-status-length content)))
  70. (cond
  71. ((>= 140 content-length) content)
  72. ((< 140 content-length) (%format-post (1- offset) post)))))
  73. (defun render-tweet (post)
  74. "Sans the url, which is a must."
  75. (funcall *tweet-format-fn* post))