;;; slack-file.el --- handle files -*- lexical-binding: t; -*- ;; Copyright (C) 2016 南優也 ;; Author: 南優也 ;; 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 . ;;; Commentary: ;; ;;; Code: (require 'eieio) (require 'slack-room) (defconst slack-file-list-url "https://slack.com/api/files.list") (defconst slack-file-upload-url "https://slack.com/api/files.upload") (defconst slack-file-delete-url "https://slack.com/api/files.delete") (defclass slack-file (slack-message) ((id :initarg :id) (created :initarg :created) (name :initarg :name) (size :initarg :size) (public :initarg :public) (filetype :initarg :filetype) (user :initarg :user) (preview :initarg :preview) (initial-comment :initarg :initial_comment :initform nil) (permalink :initarg :permalink) (channels :initarg :channels :type list) (groups :initarg :groups :type list) (ims :initarg :ims :type list) (username :initarg :username))) (defclass slack-file-room (slack-room) ()) (defun slack-file-room-obj (team) (with-slots (file-room) team (if file-room file-room (setq file-room (slack-file-room "file-room" :name "Files" :id "F" :team-id (oref team id) :created (format-time-string "%s") :last_read "0" :latest nil :unread_count 0 :unread_count_display 0 :messages '()))))) (defun slack-file-create (payload) (plist-put payload :channels (append (plist-get payload :channels) nil)) (plist-put payload :groups (append (plist-get payload :groups) nil)) (plist-put payload :ims (append (plist-get payload :ims) nil)) (plist-put payload :reactions (append (plist-get payload :reactions) nil)) (plist-put payload :pinned_to (append (plist-get payload :pinned_to) nil)) (plist-put payload :ts (number-to-string (plist-get payload :timestamp))) (let ((file (apply #'slack-file "file" (slack-collect-slots 'slack-file payload)))) (oset file reactions (mapcar #'slack-reaction-create (plist-get payload :reactions))) file)) (defmethod slack-message-equal ((f slack-file) other) (string= (oref f id) (oref other id))) (defmethod slack-file-pushnew ((f slack-file) team) (let ((room (slack-file-room-obj team))) (with-slots (messages) room (cl-pushnew f messages :test #'slack-message-equal)))) (defmethod slack-message-body ((file slack-file) team) (with-slots (initial-comment) file (let ((body (plist-get initial-comment :comment))) (slack-message-unescape-string body team)))) (defmethod slack-message-to-string ((file slack-file) team) (with-slots (ts name size filetype permalink user initial-comment reactions) file (let* ((header (slack-user-name user team)) (body (format "name: %s\nsize: %s\ntype: %s\n%s\n" name size filetype permalink)) (reactions-str (slack-message-reactions-to-string reactions))) (slack-message-put-header-property header) (slack-message-put-text-property body) (slack-message-put-reactions-property reactions-str) (let ((message (concat header "\n" body (if initial-comment (format "comment: %s\n%s\n" (slack-user-name (plist-get initial-comment :user) team) (slack-message-body file team))) (if reactions-str (concat "\n" reactions-str "\n"))))) (put-text-property 0 (length message) 'ts ts message) message)))) (defmethod slack-room-update-mark ((_room slack-file-room) _team _msg)) (defun slack-file-create-buffer (team) (funcall slack-buffer-function (slack-buffer-create (slack-file-room-obj team) team :type 'info))) (defun slack-file-list () (interactive) (let* ((team (slack-team-select)) (room (slack-file-room-obj team))) (with-slots (messages) room (if messages (slack-file-create-buffer team) (slack-room-history room team nil #'(lambda () (slack-file-create-buffer team))))))) (defmethod slack-room-history ((room slack-file-room) team &optional oldest after-success async) (cl-labels ((on-file-list (&key data &allow-other-keys) (slack-request-handle-error (data "slack-file-list") (let ((files (cl-loop for e across (plist-get data :files) collect (slack-file-create e)))) (if oldest (slack-room-set-prev-messages room files) (slack-room-update-last-read room (make-instance 'slack-message :ts "0")) (slack-room-set-messages room files))) (if after-success (funcall after-success))))) (slack-request slack-file-list-url team :params (list (if oldest (cons "ts_to" oldest))) :success #'on-file-list :sync (if async nil t)))) (defun slack-file-upload () (interactive) (cl-labels ((on-file-upload (&key data &allow-other-keys) (slack-request-handle-error (data "slack-file-upload"))) (select-channels (channels acc) (let ((selected (completing-read "Select Channel: " channels nil t))) (if (< 0 (length selected)) (select-channels channels (push selected acc)) acc))) (channel-id (selected channels) (oref (cdr (cl-assoc selected channels :test #'string=)) id))) (let* ((team (slack-team-select)) (channels (slack-room-names (append (oref team ims) (oref team channels) (oref team groups)))) (target-channels (select-channels channels '())) (channel-ids (mapconcat #'(lambda (selected) (channel-id selected channels)) (cl-delete-if #'null target-channels) ",")) (buf (find-file-noselect (car (find-file-read-args "Select File: " (confirm-nonexistent-file-or-buffer))))) (filename (read-from-minibuffer "Filename: " (file-name-nondirectory (buffer-file-name buf)))) (filetype (read-from-minibuffer "Filetype: " (file-name-extension (buffer-file-name buf)))) (initial-comment (read-from-minibuffer "Message: "))) (slack-request slack-file-upload-url team :type "POST" :params (list (cons "filename" filename) (cons "channels" channel-ids) (cons "filetype" filetype) (if initial-comment (cons "initial_comment" initial-comment))) :files (list (cons "file" buf)) :headers (list (cons "Content-Type" "multipart/form-data")) :success #'on-file-upload :sync nil)))) (defun slack-file-delete () (interactive) (cl-labels ((on-file-delete (&key data &allow-other-keys) (slack-request-handle-error (data "slack-file-delete")))) (let* ((team (slack-team-select)) (files (oref (slack-file-room-obj team) messages)) (your-files (cl-remove-if #'(lambda (f) (not (string= (oref f user) (oref team self-id)))) files)) (candidates (mapcar #'(lambda (f) (cons (concat (slack-message-time-to-string (oref f ts)) " " (oref f name)) f)) your-files)) (selected (completing-read "Select File: " candidates)) (deleting-file (cdr (cl-assoc selected candidates :test #'string=)))) (slack-request slack-file-delete-url team :params (list (cons "file" (oref deleting-file id))) :sync nil :success #'on-file-delete)))) (provide 'slack-file) ;;; slack-file.el ends here