about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el332
1 files changed, 332 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el b/configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el
new file mode 100644
index 0000000000..86ed77b6b9
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/slack-20180712.2222/slack-thread.el
@@ -0,0 +1,332 @@
+;;; 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