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

twitter.lisp 3.9KB

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