Init-Files/.emacs.d/quip.el
2016-12-12 07:20:04 -08:00

178 lines
6.6 KiB
EmacsLisp

;; Quip API
(require 'cl-extra)
(require 'json)
(require 'url)
;; Customization groups
(defgroup quip-api nil
"Customization options for using the Quip API")
(defcustom quip-api-key nil
"Your API key for Quip. Get it from https://fb.quip.com/api/personal-token"
:type 'string)
(defun quip-invoke-json (path method params)
"Submit a request to the Quip API. Returns the parsed JSON from the response."
(if (not quip-api-key)
(error "%s"
"The custom variable quip-api-key is undefined. Use custom-set-variable to set it before using quip."))
(let
((url (concat "https://platform.quip.com/1/" path))
(url-request-method method)
(url-request-extra-headers `(("Authorization" . ,(concat "Bearer " quip-api-key))
("Content-Type" . "application/x-www-form-urlencoded")))
(url-request-data
(mapconcat (lambda (pair) (format "%s=%s" (car pair) (url-hexify-string (cdr pair))))
params
"&")))
(with-current-buffer (url-retrieve-synchronously url)
(goto-char (point-min))
(re-search-forward "^$")
(json-read))))
(defun quip-new-document (content &optional format)
"Create a new Quip document with the provided content. Returns the parsed JSON response."
(quip-invoke-json "threads/new-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content))))
(defun quip-get-thread (id)
"Get the Quip thread with the specified ID. Returns the parsed JSON response."
(quip-invoke-json (concat "threads/" id) "GET" nil))
(defconst quip-location-append 0)
(defconst quip-location-prepend 1)
(defconst quip-location-after-section 2)
(defconst quip-location-before-section 3)
(defconst quip-location-replace-section 4)
(defconst quip-location-delete-section 5)
(defun quip-thread-append (thread content &optional format)
"Append the content to the specified thread."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-append)
(thread_id . ,thread))))
(defun quip-thread-prepend (thread content &optional format)
"Prepend the content to the specified thread."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-prepend)
(thread_id . ,thread))))
(defun quip-thread-append-after (thread section content &optional format)
"Append the content to the specified thread after the specified section."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-append-section)
(section_id . ,section)
(thread_id . ,thread))))
(defun quip-thread-prepend-before (thread section content &optional format)
"Prepend the content to the specified thread before the specified section."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-prepend-section)
(section_id . ,section)
(thread_id . ,thread))))
(defun quip-thread-replace-section (thread section content &optional format)
"Replace the content of the specified section."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-replace-section)
(section_id . ,section)
(thread_id . ,thread))))
(defun quip-thread-delete-section (thread section &optional format)
"Delete the specified section."
(quip-invoke-json "threads/edit-document"
"POST"
`((format . ,(or format "markdown"))
(content . ,content)
(location . ,quip-location-delete-section)
(section_id . ,section)
(thread_id . ,thread))))
;;; Content parsing functions
(defun quip-get-item-type (item)
(let ((elem-type (car item))
(attribs (cadr item)))
(cond
((eq elem-type 'p) 'paragraph)
((eq elem-type 'h1) 'h1)
((eq elem-type 'h2) 'h2)
((eq elem-type 'h3) 'h3)
((eq elem-type 'blockquote) 'block-quote)
((eq elem-type 'q) 'pull-quote)
((eq elem-type 'pre) 'code-block)
((eq elem-type 'li) 'list-item)
((eq elem-type 'span) 'span)
((eq elem-type 'div)
(letrec ((style (assoc-default 'data-section-style attribs))
(inner (caddr item))
(inner-elem-type (car inner)))
(cond
((eq inner-elem-type 'ul) 'ul)
((eq inner-elem-type 'ol) 'ol)
(t 'unrecognized-inner))))
(t 'unrecognized))))
(defun quip-get-item-id (type item)
(let ((attribs (cadr item)))
(cond
((or (eq type 'ul) ;; Nested IDs.
(eq type 'ol))
(letrec ((inner (caddr item))
(inner-attribs (cadr inner)))
(assoc-default 'id inner-attribs)))
(t (assoc-default 'id attribs)))))
(defun quip-get-item-content (type item)
(cond
((or (eq type 'ul) ;; Nested Content
(eq type 'ol))
(letrec ((inner (caddr item))
(inner-elems (cddr inner)))
(mapcar #'quip-get-item-from-element inner-elems)))
(t (caddr item))))
(defun quip-get-item-from-element (element)
(letrec
((item-type (quip-get-item-type element))
(item-id (quip-get-item-id item-type element))
(item-content (quip-get-item-content item-type element)))
`(,item-type ,item-id ,item-content)))
(defun quip-parse-html-content (content)
(with-temp-buffer
(insert content)
(letrec
((html (libxml-parse-html-region (point-min) (point-max)))
(raw-items (cddr (caddr html)))
(html-items (remove-if #'stringp raw-items)))
(mapcar #'quip-get-item-from-element html-items)
)))
;; (prin1
;; (quip-parse-html-content
;; (assoc-default 'html (quip-get-thread "idflAWG6R6Uu"))))
(provide 'quip-api)