tvl-depot/configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el

333 lines
14 KiB
EmacsLisp
Raw Normal View History

;;; slack-thread.el --- -*- lexical-binding: t; -*-
;; Copyright (C) 2017 南優也
;; Author: 南優也 <yuyaminami@minamiyuuya-no-MacBook.local>
;; Keywords:
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'eieio)
(require 'lui)
(require 'slack-util)
(require 'slack-room)
(require 'slack-channel)
(require 'slack-im)
(require 'slack-message)
(require 'slack-request)
(defvar lui-prompt-string "> ")
(defconst all-threads-url "https://slack.com/api/subscriptions.thread.getView")
(defconst thread-mark-url "https://slack.com/api/subscriptions.thread.mark")
(defcustom slack-thread-also-send-to-room 'ask
"Whether a thread message should also be sent to its room.
If nil: don't send to the room.
If `ask': ask the user every time.
Any other non-nil value: send to the room."
:type '(choice (const :tag "Never send message to the room." nil)
(const :tag "Ask the user every time." ask)
(const :tag "Always send message to the room." t)))
(define-derived-mode slack-thread-mode slack-mode "Slack - Thread"
""
(lui-set-prompt lui-prompt-string)
(setq lui-input-function 'slack-thread-message--send))
(defclass slack-thread ()
((thread-ts :initarg :thread_ts :initform "")
(messages :initarg :messages :initform '())
(has-unreads :initarg :has_unreads :initform nil)
(mention-count :initarg :mention_count :initform 0)
(reply-count :initarg :reply_count :initform 0)
(replies :initarg :replies :initform '())
(active :initarg :active :initform t)
(root :initarg :root :type slack-message)
(unread-count :initarg :unread_count :initform 0)
(last-read :initarg :last_read :initform "0")))
(defmethod slack-thread-messagep ((m slack-message))
(if (and (oref m thread-ts) (not (slack-message-thread-parentp m)))
t
nil))
(defun slack-thread-start ()
(interactive)
(slack-if-let* ((buf slack-current-buffer))
(slack-buffer-start-thread buf (slack-get-ts))))
(defun slack-thread-message--send (message)
(slack-if-let* ((buf slack-current-buffer))
(slack-buffer-send-message buf message)))
(defun slack-thread-send-message (room team message thread-ts)
(let ((message (slack-message-prepare-links
(slack-escape-message message)
team))
(broadcast (if (eq slack-thread-also-send-to-room 'ask)
(y-or-n-p (format "Also send to %s ? "
(slack-room-name room)))
slack-thread-also-send-to-room)))
(progn
(slack-message-inc-id team)
(with-slots (message-id sent-message self-id) team
(let* ((payload (list :id message-id
:channel (oref room id)
:reply_broadcast broadcast
:thread_ts thread-ts
:type "message"
:user self-id
:text message))
(json (json-encode payload))
(obj (slack-message-create payload team :room room)))
(slack-ws-send json team)
(puthash message-id obj sent-message))))))
(defun slack-thread-show-or-create ()
(interactive)
(slack-if-let* ((buf slack-current-buffer))
(if (slack-thread-message-buffer-p buf)
(error "Already in thread")
(slack-buffer-display-thread buf (slack-get-ts)))))
(cl-defmethod slack-thread-request-messages ((thread slack-thread) room team &key after-success)
(cl-labels
((on-success (&key data &allow-other-keys)
(slack-request-handle-error
(data "slack-thread-request-messages")
(let ((messages (mapcar #'(lambda (payload)
(slack-message-create payload
team
:room room))
(plist-get data :messages))))
(oset thread messages
(slack-room-sort-messages
(cl-remove-if #'slack-message-thread-parentp
messages)))))
(if after-success
(funcall after-success))))
(slack-request
(slack-request-create
(slack-room-replies-url room)
team
:params (list (cons "thread_ts" (oref thread thread-ts))
(cons "channel" (oref room id)))
:success #'on-success))))
(defmethod slack-thread-show-messages ((thread slack-thread) room team)
(cl-labels
((after-success ()
(let ((buf (slack-create-thread-message-buffer
room team (oref thread thread-ts))))
(slack-buffer-display buf))))
(slack-thread-request-messages thread room team
:after-success #'after-success)))
(defmethod slack-thread-to-string ((m slack-message) team)
(with-slots (thread) m
(if thread
(let* ((usernames (mapconcat #'identity
(cl-remove-duplicates
(mapcar #'(lambda (reply)
(slack-user-name
(plist-get reply :user)
team))
(oref thread replies))
:test #'string=)
" "))
(text (format "\n%s reply from %s"
(oref thread reply-count)
usernames)))
(propertize text
'face '(:underline t)
'keymap (let ((map (make-sparse-keymap)))
(define-key map [mouse-1] #'slack-thread-show-or-create)
(define-key map (kbd "RET") #'slack-thread-show-or-create)
map)))
"")))
(defmethod slack-thread-create ((m slack-message) team &optional payload)
(if payload
(let ((replies (plist-get payload :replies))
(reply-count (plist-get payload :reply_count))
(unread-count (plist-get payload :unread_count))
(last-read (plist-get payload :last_read)))
(make-instance 'slack-thread
:thread_ts (oref m ts)
:root m
:replies replies
:reply_count (or reply-count 0)
:unread_count (or unread-count 1)
:last_read last-read))
(make-instance 'slack-thread
:thread_ts (oref m ts)
:root m)))
(defmethod slack-merge ((old slack-thread) new)
(oset old replies (oref new replies))
(oset old reply-count (oref new reply-count))
(oset old unread-count (oref new unread-count)))
(defun slack-thread-update-state (payload team)
(slack-if-let* ((message-payload (plist-get payload :message))
(thread-ts (plist-get message-payload :thread_ts))
(room (slack-room-find (plist-get payload :channel) team))
(message (slack-room-find-message room thread-ts))
(thread (slack-message-get-thread message team))
(new-thread (slack-thread-create message team message-payload)))
(progn
(slack-merge thread new-thread)
(slack-message-update message team t t))
(message "THREAD_TS: %s, ROOM: %s, MESSAGE: %s THREAD: %s, NEW_THREAD:%s"
thread-ts
(not (null room))
(not (null message))
(not (null thread))
(not (null new-thread)))))
(defmethod slack-thread-equal ((thread slack-thread) other)
(and (string-equal (oref thread thread-ts)
(oref other thread-ts))
(string-equal (oref (oref thread root) channel)
(oref (oref other root) channel))))
(cl-defun slack-thread-get-all (&key (sync nil) (ts nil))
(let ((team (slack-team-select)))
(cl-labels
((on-success (&key data &allow-other-keys)
(slack-request-handle-error
(data "slack-thread-get-all")
(let ((threads-data (append (plist-get data :threads) nil))
(total-unread (plist-get data :total_unread_replies))
(more (if (eq :json-false (plist-get data :has_more)) nil t))
(new-count (plist-get data :new_threads_count)))
(with-slots (threads) team
(with-slots
(initializedp total-unread-replies new-threads-count has-more) threads
(setq has-more more)
(setq initializedp t)
(setq total-unread-replies total-unread)
(setq new-threads-count new-count)
(let ((parents (cl-loop for thread in threads-data
collect (slack-message-create
(plist-get thread :root_msg) team))))
(mapc #'(lambda (parent) (slack-message-update parent team nil t))
parents))))))))
(slack-request
(slack-request-create
all-threads-url
team
:type "POST"
:params (list (cons "limit" "10")
(cons "current_ts" (or ts (format-time-string "%s"))))
:success #'on-success)))))
(defmethod slack-thread-title ((thread slack-thread) team)
(with-slots (root) thread
(let ((room (slack-room-find (oref root channel) team))
(body (slack-message-body root team)))
(when room
(format "%s - %s" (slack-room-name room)
(concat (substring body 0 (min 50 (length body))) "..."))))))
(defun slack-thread-select (&optional reload)
(interactive)
(cl-labels
((load-threads (threads)
(slack-thread-get-all :sync t
:ts (cl-first
(cl-sort
(mapcar #'(lambda (thread) (oref thread thread-ts)) threads)
#'string<))))
(select-thread (threads team has-more)
(let* ((alist (cl-remove-if-not
#'(lambda (cons) (car cons))
(mapcar #'(lambda (thread)
(let ((title (slack-thread-title thread team)))
(and title (cons title thread))))
threads)))
(maybe-has-more (if has-more
(append alist (list (cons "(load more)" 'load-more))) alist))
(selected (slack-select-from-list (maybe-has-more "Select Thread: "))))
selected))
(collect-thread-parents (messages)
(mapcar #'(lambda (m) (oref m thread))
(cl-remove-if #'(lambda (m) (not (slack-message-thread-parentp m)))
messages)))
(collect-threads (team)
(cl-loop for room in (with-slots (groups ims channels) team
(append ims groups channels))
append (collect-thread-parents (oref room messages)))))
(let* ((team (slack-team-select)))
(with-slots (initializedp has-more) (oref team threads)
(if (or (not initializedp) has-more) (load-threads (collect-threads team))))
(let ((selected (select-thread (collect-threads team) team nil)))
(if (eq selected 'load-more)
(slack-thread-select t)
(slack-thread-show-messages selected
(slack-room-find (oref (oref selected root) channel) team)
team))))))
(defmethod slack-thread-delete-message ((thread slack-thread) message)
(with-slots (messages reply-count) thread
(setq messages (cl-remove-if #'(lambda (e) (string= (oref e ts) (oref message ts)))
messages))
(setq reply-count (length messages))))
(defmethod slack-thread-update-mark ((thread slack-thread) room msg team)
(with-slots (thread-ts) thread
(with-slots (id) room
(with-slots (ts) msg
(cl-labels
((on-success (&key data &allow-other-keys)
(slack-request-handle-error
(data "slack-thread-mark"))))
(slack-request
(slack-request-create
thread-mark-url
team
:params (list (cons "channel" id)
(cons "thread_ts" thread-ts)
(cons "ts" ts))
:success #'on-success)))))))
(defmethod slack-thread-marked ((thread slack-thread) payload)
(let ((unread-count (plist-get payload :unread_count))
(last-read (plist-get payload :last_read)))
(oset thread unread-count unread-count)
(oset thread last-read last-read)))
(defun slack-room-unread-threads ()
(interactive)
(slack-if-let* ((buf slack-current-buffer))
(slack-buffer-display-unread-threads buf)))
(defmethod slack-thread-update-last-read ((thread slack-thread) msg)
(with-slots (ts) msg
(oset thread last-read ts)))
(provide 'slack-thread)
;;; slack-thread.el ends here