diff options
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/mastodon-http.el | 114 | ||||
-rw-r--r-- | lisp/mastodon-media.el | 5 | ||||
-rw-r--r-- | lisp/mastodon-profile.el | 68 | ||||
-rw-r--r-- | lisp/mastodon-tl.el | 26 | ||||
-rw-r--r-- | lisp/mastodon.el | 10 |
5 files changed, 126 insertions, 97 deletions
diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index 086dcec..f32ccd4 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -73,7 +73,7 @@ "Retrieve URL asynchronously. This is a thin abstraction over the system -`url-retrieve-synchronously`. Depending on which version of this +`url-retrieve-synchronously'. Depending on which version of this is available we will call it with or without a timeout." (if (< (cdr (func-arity 'url-retrieve-synchronously)) 4) (url-retrieve-synchronously url) @@ -100,6 +100,7 @@ Message status and JSON error from RESPONSE if unsuccessful." (defmacro mastodon-http--authorized-request (method body &optional unauthenticated-p) "Make a METHOD type request using BODY, with Mastodon authorization. Unless UNAUTHENTICATED-P is non-nil." + (declare (debug 'body)) `(let ((url-request-method ,method) (url-request-extra-headers (unless ,unauthenticated-p @@ -107,6 +108,18 @@ Unless UNAUTHENTICATED-P is non-nil." (concat "Bearer " (mastodon-auth--access-token))))))) ,body)) +(defun mastodon-http--build-query-string (args) + "Build a request query string from ARGS." + ;; (url-build-query-string args nil)) + ;; url-build-query-string adds 'nil' to empty params so lets stay with our + ;; own: + (mapconcat (lambda (arg) + (concat (url-hexify-string (car arg)) + "=" + (url-hexify-string (cdr arg)))) + args + "&")) + (defun mastodon-http--post (url args headers &optional unauthenticated-p) "POST synchronously to URL with ARGS and HEADERS. @@ -115,12 +128,7 @@ Authorization header is included by default unless UNAUTHENTICATED-P is non-nil. "POST" (let ((url-request-data (when args - (mapconcat (lambda (arg) - (concat (url-hexify-string (car arg)) - "=" - (url-hexify-string (cdr arg)))) - args - "&"))) + (mastodon-http--build-query-string args))) (url-request-extra-headers (append url-request-extra-headers ; auth set in macro ;; pleroma compat: @@ -165,13 +173,6 @@ Pass response buffer to CALLBACK function." (with-temp-buffer (mastodon-http--url-retrieve-synchronously url)))) -(defun mastodon-http--append-query-string (url params) - "Append PARAMS to URL as query strings and return it. - -PARAMS should be an alist as required by `url-build-query-string'." - (let ((query-string (url-build-query-string params))) - (concat url "?" query-string))) - ;; search functions: (defun mastodon-http--process-json-search () "Process JSON returned by a search query to the server." @@ -215,7 +216,9 @@ Optionally specify the PARAMS to send." Optionally specify the PARAMS to send." (mastodon-http--authorized-request "PATCH" - (let ((url (mastodon-http--append-query-string base-url params))) + (let ((url + (concat base-url "?" + (mastodon-http--build-query-string params)))) (mastodon-http--url-retrieve-synchronously url)))) ;; Asynchronous functions @@ -244,12 +247,7 @@ Authorization header is included by default unless UNAUTHENTICED-P is non-nil." (let ((request-timeout 5) (url-request-data (when args - (mapconcat (lambda (arg) - (concat (url-hexify-string (car arg)) - "=" - (url-hexify-string (cdr arg)))) - args - "&")))) + (mastodon-http--build-query-string args)))) (with-temp-buffer (url-retrieve url callback cbargs))))) @@ -262,43 +260,43 @@ item uploaded, and `mastodon-toot--update-status-fields' is run." (let* ((file (file-name-nondirectory filename)) (request-backend 'curl)) (request - url - :type "POST" - :params `(("description" . ,caption)) - :files `(("file" . (,file :file ,filename - :mime-type "multipart/form-data"))) - :parser 'json-read - :headers `(("Authorization" . ,(concat "Bearer " - (mastodon-auth--access-token)))) - :sync nil - :success (cl-function - (lambda (&key data &allow-other-keys) - (when data - (push (alist-get 'id data) - mastodon-toot--media-attachment-ids) ; add ID to list - (message "%s file %s with id %S and caption '%s' uploaded!" - (capitalize (alist-get 'type data)) - file - (alist-get 'id data) - (alist-get 'description data)) - (mastodon-toot--update-status-fields)))) - :error (cl-function - (lambda (&key error-thrown &allow-other-keys) - (cond - ;; handle curl errors first (eg 26, can't read file/path) - ;; because the '=' test below fails for them - ;; they have the form (error . error message 24) - ((not (proper-list-p error-thrown)) ; not dotted list - (message "Got error: %s. Shit went south." (cdr error-thrown))) - ;; handle mastodon api errors - ;; they have the form (error http 401) - ((= (car (last error-thrown)) 401) - (message "Got error: %s Unauthorized: The access token is invalid" error-thrown)) - ((= (car (last error-thrown)) 422) - (message "Got error: %s Unprocessable entity: file or file type is unsupported or invalid" error-thrown)) - (t - (message "Got error: %s Shit went south" - error-thrown)))))))) + url + :type "POST" + :params `(("description" . ,caption)) + :files `(("file" . (,file :file ,filename + :mime-type "multipart/form-data"))) + :parser 'json-read + :headers `(("Authorization" . ,(concat "Bearer " + (mastodon-auth--access-token)))) + :sync nil + :success (cl-function + (lambda (&key data &allow-other-keys) + (when data + (push (alist-get 'id data) + mastodon-toot--media-attachment-ids) ; add ID to list + (message "%s file %s with id %S and caption '%s' uploaded!" + (capitalize (alist-get 'type data)) + file + (alist-get 'id data) + (alist-get 'description data)) + (mastodon-toot--update-status-fields)))) + :error (cl-function + (lambda (&key error-thrown &allow-other-keys) + (cond + ;; handle curl errors first (eg 26, can't read file/path) + ;; because the '=' test below fails for them + ;; they have the form (error . error message 24) + ((not (proper-list-p error-thrown)) ; not dotted list + (message "Got error: %s. Shit went south." (cdr error-thrown))) + ;; handle mastodon api errors + ;; they have the form (error http 401) + ((= (car (last error-thrown)) 401) + (message "Got error: %s Unauthorized: The access token is invalid" error-thrown)) + ((= (car (last error-thrown)) 422) + (message "Got error: %s Unprocessable entity: file or file type is unsupported or invalid" error-thrown)) + (t + (message "Got error: %s Shit went south" + error-thrown)))))))) (provide 'mastodon-http) ;;; mastodon-http.el ends here diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index ace15b2..9715a6c 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -186,7 +186,6 @@ with the image." MEDIA-TYPE is a symbol and either 'avatar or 'media-link. START is the position where we start loading the image. REGION-LENGTH is the range from start to propertize." - ;; TODO: Cache the avatars (let ((image-options (when (or (image-type-available-p 'imagemagick) (image-transforms-p)) ; inbuilt scaling in 27.1 (cond @@ -196,8 +195,8 @@ REGION-LENGTH is the range from start to propertize." `(:max-height ,mastodon-media--preview-max-height)))))) (let ((buffer (current-buffer)) (marker (copy-marker start)) - ;; Keep url.el from spamming us with messages about connecting to hosts: - (url-show-status nil)) + ;; Keep url.el from spamming us with messages about connecting to hosts: + (url-show-status nil)) (condition-case nil ;; catch any errors in url-retrieve so as to not abort ;; whatever called us diff --git a/lisp/mastodon-profile.el b/lisp/mastodon-profile.el index 3b6f336..ff729f0 100644 --- a/lisp/mastodon-profile.el +++ b/lisp/mastodon-profile.el @@ -274,7 +274,7 @@ Both args are strings. SOURCE means that the preference is in the 'source' part of the account JSON." (let* ((url (mastodon-http--api "accounts/update_credentials")) (pref-formatted (if source (concat "source[" pref "]") pref)) - (response (mastodon-http--patch url `((,pref-formatted ,val))))) + (response (mastodon-http--patch url `((,pref-formatted . ,val))))) (mastodon-http--triage response (lambda () (mastodon-profile-fetch-server-account-settings) @@ -365,6 +365,15 @@ Current settings are fetched from the server." (interactive) (mastodon-profile--edit-string-value 'display_name)) +(defun mastodon-profile--get-preferences-pref (pref) + "Fetch PREF from the endpoint \"/preferences\". +This endpoint only holds a few preferences. For others, see +`mastodon-profile--update-preference' and its endpoint, +\"/accounts/update_credentials.\"" + (alist-get pref + (mastodon-http--get-json + (mastodon-http--api "preferences")))) + (defun mastodon-profile-view-preferences () "View user preferences in another window." (interactive) @@ -397,11 +406,11 @@ Current settings are fetched from the server." (defun mastodon-profile--fields-get (account) "Fetch the fields vector (aka profile metadata) from profile of ACCOUNT. -Returns a list of lists." +Returns an alist." (let ((fields (mastodon-profile--account-field account 'fields))) (when fields (mapcar (lambda (el) - (list (alist-get 'name el) + (cons (alist-get 'name el) (alist-get 'value el))) fields)))) @@ -415,7 +424,7 @@ Returns a list of lists." (concat (format "_ %s " (car field)) (make-string (- (+ 1 left-width) (length (car field))) ?_) - (format " :: %s" (cadr field))) + (format " :: %s" (cdr field))) field)) ; hack to make links tabstops fields ""))) @@ -477,30 +486,33 @@ Returns a list of lists." (is-followers " FOLLOWERS ") (is-following " FOLLOWING ")))) (insert - "\n" - (mastodon-profile--image-from-account account) - "\n" - (propertize (mastodon-profile--account-field - account 'display_name) - 'face 'mastodon-display-name-face) - "\n" - (propertize (concat "@" acct) - 'face 'default) - (if (equal locked t) - (if (fontp (char-displayable-p #10r9993)) - " 🔒" - " [locked]") - "") - "\n ------------\n" - (mastodon-tl--render-text note account) - ;; account here to enable tab-stops in profile note - (if fields - (concat "\n" - (mastodon-tl--set-face - (mastodon-profile--fields-insert fields) - 'success) - "\n") - "") + (propertize + (concat + "\n" + (mastodon-profile--image-from-account account) + "\n" + (propertize (mastodon-profile--account-field + account 'display_name) + 'face 'mastodon-display-name-face) + "\n" + (propertize (concat "@" acct) + 'face 'default) + (if (equal locked t) + (if (fontp (char-displayable-p #10r9993)) + " 🔒" + " [locked]") + "") + "\n ------------\n" + (mastodon-tl--render-text note account) + ;; account here to enable tab-stops in profile note + (if fields + (concat "\n" + (mastodon-tl--set-face + (mastodon-profile--fields-insert fields) + 'success) + "\n") + "")) + 'profile-json account) ;; insert counts (mastodon-tl--set-face (concat " ------------\n" diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index d8b2baa..158ba5f 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -66,6 +66,7 @@ (autoload 'mastodon-search--get-user-info "mastodon-search") (autoload 'mastodon-http--delete "mastodon-http") (autoload 'mastodon-profile--view-author-profile "mastodon-profile") +(autoload 'mastodon-profile--get-preferences-pref "mastodon-profile") (when (require 'mpv nil :no-error) (declare-function mpv-start "mpv")) @@ -646,7 +647,9 @@ START and END are the boundaries of the link in the toot." keymap (help-echo (get-text-property start 'help-echo)) extra-properties - (toot-url (mastodon-tl--field 'url toot)) + ;; handle calling this on non-toots, e.g. for profiles: + (toot-url (when (proper-list-p toot) + (mastodon-tl--field 'url toot))) (toot-url (when toot-url (url-generic-parse-url toot-url))) (toot-instance-url (if toot-url (concat (url-type toot-url) "://" @@ -665,8 +668,10 @@ START and END are the boundaries of the link in the toot." (;; User handles: maybe-userhandle ;; this fails on mentions in profile notes: - (let ((maybe-userid (mastodon-tl--extract-userid-toot - toot maybe-userhandle))) + (let ((maybe-userid + (when (proper-list-p toot) + (mastodon-tl--extract-userid-toot + toot maybe-userhandle)))) (setq mastodon-tab-stop-type 'user-handle keymap mastodon-tl--link-keymap help-echo (concat "Browse user profile of " maybe-userhandle) @@ -856,7 +861,12 @@ message is a link which unhides/hides the main body." (concat cw (propertize (mastodon-tl--content toot) - 'invisible t + 'invisible + ;; check server setting to expand all spoilers: + (unless (eq t + (mastodon-profile--get-preferences-pref + 'reading:expand:spoilers)) + t) 'mastodon-content-warning-body t)))) (defun mastodon-tl--media (toot) @@ -1694,7 +1704,13 @@ For use after e.g. deleting a toot." (mastodon-notifications--get)) ((equal (mastodon-tl--buffer-name) (concat "*mastodon-" (mastodon-auth--get-account-name) "-statuses*")) - (mastodon-profile--my-profile)))) + (mastodon-profile--my-profile)) + ((save-match-data + (string-match + "statuses/\\(?2:[[:digit:]]+\\)/context" + (mastodon-tl--get-endpoint)) + (mastodon-tl--thread + (match-string 2 (mastodon-tl--get-endpoint))))))) (defun mastodon-tl--more () "Append older toots to timeline, asynchronously." diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 5e95b35..bc624d9 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -5,7 +5,7 @@ ;; Author: Johnson Denen <johnson.denen@gmail.com> ;; Maintainer: Marty Hiatt <martianhiatus@riseup.net> ;; Version: 1.0.0 -;; Package-Requires: ((emacs "27.1") (request "0.3.0")) +;; Package-Requires: ((emacs "27.1") (request "0.3.0") (persist "0.4")) ;; Homepage: https://codeberg.org/martianh/mastodon.el ;; This file is not part of GNU Emacs. @@ -283,7 +283,9 @@ not, just browse the URL in the normal fashion." (get-text-property (point) 'shr-url) (read-string "Lookup URL: ")))) (if (not (mastodon--masto-url-p query)) - (browse-url query) + (if (equal major-mode 'mastodon-mode) + (shr-browse-url query) ;; keep our shr keymap + (browse-url query)) (message "Performing lookup...") (let* ((url (format "%s/api/v2/search" mastodon-instance-url)) (param (concat "resolve=t")) ; webfinger @@ -305,10 +307,12 @@ not, just browse the URL in the normal fashion." (defun mastodon--masto-url-p (query) "Check if QUERY resembles a fediverse URL." ;; calqued off https://github.com/tuskyapp/Tusky/blob/c8fc2418b8f5458a817bba221d025b822225e130/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt + ;; thx to Conny Duck! (let* ((uri-parsed (url-generic-parse-url query)) (query (url-filename uri-parsed))) (save-match-data - (or (string-match "^/@[[:alnum:]]+/[[:digit:]]+$" query) + (or (string-match "^/@[^/]+$" query) + (string-match "^/@[^/]+/[[:digit:]]+$" query) (string-match "^/users/[[:alnum:]]+$" query) (string-match "^/notice/[[:alnum:]]+$" query) (string-match "^/objects/[-a-f0-9]+$" query) |