diff options
Diffstat (limited to 'lisp/mastodon-tl.el')
-rw-r--r-- | lisp/mastodon-tl.el | 207 |
1 files changed, 162 insertions, 45 deletions
diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index ce2062d..a8bccb9 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -1,6 +1,7 @@ ;;; mastodon-tl.el --- HTTP request/response functions for mastodon.el -*- lexical-binding: t -*- ;; Copyright (C) 2017-2019 Johnson Denen +;; Copyright (C) 2020-2022 Marty Hiatt ;; Author: Johnson Denen <johnson.denen@gmail.com> ;; Marty Hiatt <martianhiatus@riseup.net> ;; Maintainer: Marty Hiatt <martianhiatus@riseup.net> @@ -36,6 +37,7 @@ (require 'thingatpt) ; for word-at-point (require 'time-date) (require 'cl-lib) +(require 'mastodon-iso) (require 'mpv nil :no-error) @@ -78,6 +80,11 @@ (autoload 'mastodon-http--build-params-string "mastodon-http") (autoload 'mastodon-notifications--filter-types-list "mastodon-notifications") (autoload 'mastodon-toot--get-toot-edits "mastodon-toot") +(autoload 'mastodon-toot--update-status-fields "mastodon-toot") +(autoload 'mastodon-toot--compose-buffer "mastodon-toot") + +(defvar mastodon-toot--visibility) +(defvar mastodon-active-user) (when (require 'mpv nil :no-error) (declare-function mpv-start "mpv")) @@ -254,7 +261,7 @@ types of mastodon links and not just shr.el-generated ones.") (when (require 'mpv nil :no-error) (let ((map (make-sparse-keymap))) (define-key map (kbd "<C-return>") 'mastodon-tl--mpv-play-video-from-byline) - (define-key map (kbd "<return>") 'mastodon-profile--view-author-profile) + (define-key map (kbd "<return>") 'mastodon-profile--get-toot-author) (keymap-canonicalize map))) "The keymap to be set for the author byline. It is active where point is placed by `mastodon-tl--goto-next-toot.'") @@ -563,25 +570,25 @@ TIMESTAMP is assumed to be in the past." (relative-result (cond ((< seconds-difference 60) - (cons "less than a minute ago" + (cons "just now" 60)) ((< seconds-difference (* 1.5 60)) - (cons "one minute ago" + (cons "1 minute ago" 90)) ;; at 90 secs ((< seconds-difference (* 60 59.5)) (funcall regular-response seconds-difference 60 "minute")) ((< seconds-difference (* 1.5 60 60)) - (cons "one hour ago" + (cons "1 hour ago" (* 60 90))) ;; at 90 minutes ((< seconds-difference (* 60 60 23.5)) (funcall regular-response seconds-difference (* 60 60) "hour")) ((< seconds-difference (* 1.5 60 60 24)) - (cons "one day ago" + (cons "1 day ago" (* 1.5 60 60 24))) ;; at a day and a half ((< seconds-difference (* 60 60 24 6.5)) (funcall regular-response seconds-difference (* 60 60 24) "day")) ((< seconds-difference (* 1.5 60 60 24 7)) - (cons "one week ago" + (cons "1 week ago" (* 1.5 60 60 24 7))) ;; a week and a half ((< seconds-difference (* 60 60 24 7 52)) (if (= 52 (floor (+ 0.5 (/ seconds-difference 60 60 24 7)))) @@ -589,7 +596,7 @@ TIMESTAMP is assumed to be in the past." (* 60 60 24 7 52)) (funcall regular-response seconds-difference (* 60 60 24 7) "week"))) ((< seconds-difference (* 1.5 60 60 24 365)) - (cons "one year ago" + (cons "1 year ago" (* 60 60 24 365 1.5))) ;; a year and a half (t (funcall regular-response seconds-difference (* 60 60 24 365.25) "year"))))) @@ -1156,7 +1163,7 @@ this just means displaying toot client." (let* ((poll (mastodon-tl--field 'poll toot)) (expiry (mastodon-tl--field 'expires_at poll)) (expired-p (if (eq (mastodon-tl--field 'expired poll) :json-false) nil t)) - (multi (mastodon-tl--field 'multiple poll)) + ;; (multi (mastodon-tl--field 'multiple poll)) (voters-count (mastodon-tl--field 'voters_count poll)) (vote-count (mastodon-tl--field 'votes_count poll)) (options (mastodon-tl--field 'options poll)) @@ -1368,10 +1375,12 @@ BUFFER is buffer name, ENDPOINT is buffer's enpoint, UPDATE-FUNCTION is its update function. LINK-HEADER is the http Link header if present." (setq mastodon-tl--buffer-spec - `(buffer-name ,buffer - endpoint ,endpoint - update-function ,update-function - link-header ,link-header))) + `(account ,(cons mastodon-active-user + mastodon-instance-url) + buffer-name ,buffer + endpoint ,endpoint + update-function ,update-function + link-header ,link-header))) (defun mastodon-tl--more-json (endpoint id) "Return JSON for timeline ENDPOINT before ID." @@ -1451,7 +1460,7 @@ ID is that of the toot to view." (mastodon-mode) (mastodon-tl--set-buffer-spec buffer (format "statuses/%s" id) - (lambda (_toot) (message "END of thread."))) + nil) (let ((inhibit-read-only t)) (mastodon-tl--toot toot :detailed-p)))))) @@ -1466,7 +1475,8 @@ ID is that of the toot to view." (if (or (string= type "follow_request") (string= type "follow")) ; no can thread these (error "No thread") - (let* ((url (mastodon-http--api (format "statuses/%s/context" id))) + (let* ((endpoint (format "statuses/%s/context" id)) + (url (mastodon-http--api endpoint)) (buffer (format "*mastodon-thread-%s*" id)) (toot ;; refetch current toot in case we just faved/boosted: @@ -1488,10 +1498,9 @@ ID is that of the toot to view." (with-output-to-temp-buffer buffer (switch-to-buffer buffer) (mastodon-mode) - (mastodon-tl--set-buffer-spec - buffer - (format "statuses/%s/context" id) - (lambda (_toot) (message "END of thread."))) + (mastodon-tl--set-buffer-spec buffer + endpoint + nil) (let ((inhibit-read-only t)) (mastodon-tl--timeline (alist-get 'ancestors context)) (goto-char (point-max)) @@ -1505,6 +1514,65 @@ ID is that of the toot to view." ;; else just print the lone toot: (mastodon-tl--single-toot id))))))) + +(defun mastodon-tl--mute-thread () + "Mute the thread displayed in the current buffer. +Note that you can only (un)mute threads you have posted in." + (interactive) + (mastodon-tl--mute-or-unmute-thread)) + +(defun mastodon-tl--unmute-thread () + "Mute the thread displayed in the current buffer. +Note that you can only (un)mute threads you have posted in." + (interactive) + (mastodon-tl--mute-or-unmute-thread :unmute)) + +(defun mastodon-tl--mute-or-unmute-thread (&optional unmute) + "Mute a thread. +If UNMUTE, unmute it." + (let ((endpoint (mastodon-tl--get-endpoint))) + (if (string-suffix-p "context" endpoint) ; thread view + (let* ((id + (save-match-data + (string-match "statuses/\\(?2:[[:digit:]]+\\)/context" + endpoint) + (match-string 2 endpoint))) + (we-posted-p (mastodon-tl--user-in-thread-p id)) + (url (mastodon-http--api + (if unmute + (format "statuses/%s/unmute" id) + (format "statuses/%s/mute" id))))) + (if (not we-posted-p) + (message "You can only (un)mute a thread you have posted in.") + (when (if unmute + (y-or-n-p "Unnute this thread? ") + (y-or-n-p "Mute this thread? ")) + (let ((response (mastodon-http--post url))) + (mastodon-http--triage response + (lambda () + (if unmute + (message "Thread unmuted!") + (message "Thread muted!"))))))))))) + +(defun mastodon-tl--user-in-thread-p (id) + "Return non-nil if the logged-in user has posted to the current thread. +ID is that of the post the context is currently displayed for." + (let* ((context-json (mastodon-http--get-json + (mastodon-http--api (format "statuses/%s/context" id)) + nil :silent)) + (ancestors (alist-get 'ancestors context-json)) + (descendants (alist-get 'descendants context-json)) + (a-ids (mapcar (lambda (status) + (alist-get 'id + (alist-get 'account status))) + ancestors)) + (d-ids (mapcar (lambda (status) + (alist-get 'id + (alist-get 'account status))) + descendants))) + (or (member (mastodon-auth--get-account-id) a-ids) + (member (mastodon-auth--get-account-id) d-ids)))) + ;;; LISTS (defun mastodon-tl--get-users-lists () @@ -1951,6 +2019,9 @@ INSTANCE is an instance domain name." (let ((buf (get-buffer-create "*mastodon-instance*"))) (with-current-buffer buf (switch-to-buffer-other-window buf) + (mastodon-tl--set-buffer-spec (buffer-name buf) + "instance" + nil) (let ((inhibit-read-only t)) (erase-buffer) (special-mode) @@ -2073,16 +2144,18 @@ IND is the optional indentation level to print at." ;;; FOLLOW/BLOCK/MUTE, ETC -(defun mastodon-tl--follow-user (user-handle &optional notify) +(defun mastodon-tl--follow-user (user-handle &optional notify langs) "Query for USER-HANDLE from current status and follow that user. If NOTIFY is \"true\", enable notifications when that user posts. If NOTIFY is \"false\", disable notifications when that user posts. -Can be called to toggle NOTIFY on users already being followed." +Can be called to toggle NOTIFY on users already being followed. +LANGS is an array parameters alist of languages to filer user's posts by." (interactive (list (mastodon-tl--interactive-user-handles-get "follow"))) (mastodon-tl--do-if-toot - (mastodon-tl--do-user-action-and-response user-handle "follow" nil notify))) + (mastodon-tl--do-user-action-and-response + user-handle "follow" nil notify langs))) (defun mastodon-tl--enable-notify-user-posts (user-handle) "Query for USER-HANDLE and enable notifications when they post." @@ -2099,6 +2172,33 @@ Can be called to toggle NOTIFY on users already being followed." (mastodon-tl--interactive-user-handles-get "disable"))) (mastodon-tl--follow-user user-handle "false")) +(defun mastodon-tl--filter-user-user-posts-by-language (user-handle) + "Query for USER-HANDLE and enable notifications when they post. +This feature is experimental and for now not easily varified by +the instance API." + (interactive + (list + (mastodon-tl--interactive-user-handles-get "filter by language"))) + (let ((langs (mastodon-tl--read-filter-langs))) + (mastodon-tl--do-if-toot + (mastodon-tl--follow-user user-handle nil langs)))) + +(defun mastodon-tl--read-filter-langs (&optional langs) + "Read language choices and return an alist array parameter. +LANGS is the accumulated array param alist if we re-run recursively." + (let* ((langs-alist langs) + (choice (completing-read "Filter user's posts by language: " + mastodon-iso-639-1))) + (when choice + (setq langs-alist + (push `("languages[]" . ,(alist-get choice mastodon-iso-639-1 + nil nil + #'string=)) + langs-alist)) + (if (y-or-n-p "Filter by another language? ") + (mastodon-tl--read-filter-langs langs-alist) + langs-alist)))) + (defun mastodon-tl--unfollow-user (user-handle) "Query for USER-HANDLE from current status and unfollow that user." (interactive @@ -2141,6 +2241,16 @@ Can be called to toggle NOTIFY on users already being followed." (message "Looks like you have no mutes to unmute!") (mastodon-tl--do-user-action-and-response user-handle "unmute" t))) +(defun mastodon-tl--dm-user (user-handle) + "Query for USER-HANDLE from current status and compose a message to that user." + (interactive + (list + (mastodon-tl--interactive-user-handles-get "message"))) + (mastodon-tl--do-if-toot + (mastodon-toot--compose-buffer (concat "@" user-handle)) + (setq mastodon-toot--visibility "direct") + (mastodon-toot--update-status-fields))) + (defun mastodon-tl--interactive-user-handles-get (action) "Get the list of user-handles for ACTION from the current toot." (mastodon-tl--do-if-toot @@ -2191,12 +2301,13 @@ Action must be either \"unblock\" or \"unmute\"." nil ; predicate t)))) -(defun mastodon-tl--do-user-action-and-response (user-handle action &optional negp notify) +(defun mastodon-tl--do-user-action-and-response (user-handle action &optional negp notify langs) "Do ACTION on user USER-HANDLE. NEGP is whether the action involves un-doing something. If NOTIFY is \"true\", enable notifications when that user posts. If NOTIFY is \"false\", disable notifications when that user posts. -NOTIFY is only non-nil when called by `mastodon-tl--follow-user'." +NOTIFY is only non-nil when called by `mastodon-tl--follow-user'. +LANGS is an array parameters alist of languages to filer user's posts by." (let* ((account (if negp ;; if unmuting/unblocking, we got handle from mute/block list (mastodon-profile--search-account-by-handle @@ -2212,35 +2323,41 @@ NOTIFY is only non-nil when called by `mastodon-tl--follow-user'." (name (if (not (string-empty-p (mastodon-profile--account-field account 'display_name))) (mastodon-profile--account-field account 'display_name) (mastodon-profile--account-field account 'username))) - (url (mastodon-http--api - (if notify - (format "accounts/%s/%s?notify=%s" user-id action notify) - (format "accounts/%s/%s" user-id action))))) + (args (cond (notify + `(("notify" . ,notify))) + (langs langs) + (t nil))) + (url (mastodon-http--api (format "accounts/%s/%s" user-id action)))) (if account (if (equal action "follow") ; y-or-n for all but follow - (mastodon-tl--do-user-action-function url name user-handle action notify) + (mastodon-tl--do-user-action-function url name user-handle action notify args) (when (y-or-n-p (format "%s user %s? " action name)) - (mastodon-tl--do-user-action-function url name user-handle action))) + (mastodon-tl--do-user-action-function url name user-handle action args))) (message "Cannot find a user with handle %S" user-handle)))) -(defun mastodon-tl--do-user-action-function (url name user-handle action &optional notify) +(defun mastodon-tl--do-user-action-function (url name user-handle action &optional notify args) "Post ACTION on user NAME/USER-HANDLE to URL. NOTIFY is either \"true\" or \"false\", and used when we have been called -by `mastodon-tl--follow-user' to enable or disable notifications." - (let ((response (mastodon-http--post url))) - (mastodon-http--triage response - (lambda () - (cond ((string-equal notify "true") - (message "Receiving notifications for user %s (@%s)!" - name user-handle)) - ((string-equal notify "false") - (message "Not receiving notifications for user %s (@%s)!" - name user-handle)) - ((or (string-equal action "mute") - (string-equal action "unmute")) - (message "User %s (@%s) %sd!" name user-handle action)) - ((eq notify nil) - (message "User %s (@%s) %sed!" name user-handle action))))))) +by `mastodon-tl--follow-user' to enable or disable notifications. +ARGS is an alist of any parameters to send with the request." + (let ((response (mastodon-http--post url args))) + (mastodon-http--triage + response + (lambda () + (cond ((string-equal notify "true") + (message "Receiving notifications for user %s (@%s)!" + name user-handle)) + ((string-equal notify "false") + (message "Not receiving notifications for user %s (@%s)!" + name user-handle)) + ((or (string-equal action "mute") + (string-equal action "unmute")) + (message "User %s (@%s) %sd!" name user-handle action)) + ((assoc "languages[]" args #'equal) + (message "User %s filtered by language(s): %s" name + (mapconcat #'cdr args " "))) + ((eq notify nil) + (message "User %s (@%s) %sed!" name user-handle action))))))) ;; FOLLOW TAGS |