-
Notifications
You must be signed in to change notification settings - Fork 0
/
org-twitter.el
129 lines (116 loc) · 5.38 KB
/
org-twitter.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
;;; org-twitter.el --- Tweet from org-mode -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2020 Ketan Agrawal
;;
;; Author: Ketan Agrawal <http://github.com/ketan0>
;; Maintainer: Ketan Agrawal <[email protected]>
;; Created: October 13, 2020
;; Modified: November 28, 2020
;; Version: 0.0.1
;; Keywords:
;; Homepage: https://github.com/ketan0/org-twitter
;; Package-Requires: ((emacs 26.1) (org-ml "4.0") (cl-lib "0.5") (aio "1.0") (twittering-mode "3.1"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Tweet from org-mode.
;;
;;; Code:
(require 'cl-lib)
(require 'twittering-mode)
(require 'org-ml)
(require 'aio)
(defgroup org-twitter nil
"Convert org-mode notes to tweets and threads."
:group 'org
:prefix "org-twitter-"
:link '(url-link :tag "Github" "https://github.com/ketan0/org-twitter"))
(defcustom org-twitter-tweet-link-description "🐦"
"Link description that is appended following tweeted text."
:group 'org-twitter
:type 'string)
(aio-defun org-twitter-tweet-selection (beg end)
"Interactively tweet the currently selected text."
(interactive "r")
(let* ((saved-buffer (current-buffer))
(status (buffer-substring-no-properties beg end))
(status-id (alist-get 'id_str (aio-wait-for (org-twitter-tweet status)))))
(org-twitter-add-tweet-link-to-selection saved-buffer beg end status status-id)))
(defun org-twitter-add-tweet-link-to-selection (saved-buffer beg end status status-id)
(with-current-buffer saved-buffer
(delete-region beg end)
(insert (org-twitter-linkify-tweet status status-id))))
(aio-defun org-twitter-tweet-this-headline (saved-point)
"Interactively tweet the headline under point."
(interactive "d")
(let* ((saved-buffer (current-buffer))
(status (org-ml-get-property :raw-value (org-ml-parse-this-headline)))
(status-id (alist-get 'id_str (aio-await (org-twitter-tweet status)))))
(org-twitter-add-tweet-link-to-headline saved-buffer saved-point status status-id)))
(defun org-twitter-add-tweet-link-to-headline (saved-buffer saved-point status status-id)
(with-current-buffer saved-buffer
(message "updating headline in %s to %s" saved-buffer (org-twitter-linkify-tweet status status-id))
(org-ml-update-headline-at* saved-point
(org-ml-set-property :title (org-ml-build-secondary-string!
(org-twitter-linkify-tweet status status-id)) it))))
(defun org-twitter-linkify-tweet (status status-id)
;; TODO: include actual username in link rather than underscore
;; (format "[[https://twitter.com/_/status/%s][%s]]" status-id status)
(format "%s ([[https://twitter.com/_/status/%s][%s]])"
status status-id org-twitter-tweet-link-description))
(aio-defun org-twitter-tweet-subheadlines-as-thread ()
(interactive)
(let* ((saved-buffer (current-buffer))
(headlines (org-ml-headline-get-subheadlines (org-ml-parse-this-subtree)))
(headline-locations
(mapcar (lambda (headline) (org-ml-get-property :begin headline)) headlines))
(tweets
(mapcar (lambda (headline) (org-ml-get-property :raw-value headline)) headlines))
(status-ids (aio-await (org-twitter-tweet-thread tweets))))
(mapcar* (lambda (saved-point status status-id)
(org-twitter-add-tweet-link-to-headline saved-buffer saved-point status status-id))
;; visit headlines in reverse so locations don't change
(nreverse headline-locations) (nreverse tweets) (nreverse status-ids))))
(aio-defun org-twitter-tweet-thread (tweets)
(let ((in-reply-to-status-id)
(status-ids '()))
(while tweets
(let* ((status (car tweets))
(response (aio-await (org-twitter-tweet status in-reply-to-status-id))))
(message "tweeting \"%s\"" status)
(if (stringp response) (error response)
(progn (setq in-reply-to-status-id (alist-get 'id_str response))
(push in-reply-to-status-id status-ids))))
(setq tweets (cdr tweets)))
(nreverse status-ids)))
(aio-defun org-twitter-tweet (tweet-text &optional in-reply-to-status-id)
(aio-await
(org-twitter-call-api 'update-status
(append `((status . ,tweet-text))
(when in-reply-to-status-id
`((in-reply-to-status-id . ,in-reply-to-status-id)))))))
;; awaitable
;; don't need aio-defun since I'm manually creating/returning the promise
(defun org-twitter-call-api (command args-alist)
(let ((promise (aio-promise)))
(if (twittering-ensure-preparation-for-api-invocation)
(twittering-call-api command
(append args-alist
`((sentinel . ,(org-twitter-create-sentinel promise)))))
(aio-resolve promise (lambda () (error "Authorization failed"))))
promise))
(defun org-twitter-create-sentinel (promise)
(lambda (proc status connection-info header-info)
(let ((status-code (cdr (assq 'status-code header-info))))
(case-string
status-code
(("200")
(let ((json-response (twittering-json-read))) ;; don't understand this, but ok
(aio-resolve promise (lambda () json-response))))
(t
(aio-resolve promise (lambda () (format "Got erroneous response: %s"
(twittering-get-error-message header-info connection-info)))))))))
(provide 'org-twitter)
;;; org-twitter.el ends here