transmission.lisp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. (in-package #:chatikbot)
  2. (defsetting *transmission-settings* nil "ALIST of (chat-id . url)")
  3. (defvar *transmission-sessions* nil "ALIST of (url . x-transmission-session-id)")
  4. (defun transmission-get-session (url)
  5. (multiple-value-bind (body status headers uri stream)
  6. (drakma:http-request url :force-binary t :decode-content t)
  7. (declare (ignore body status uri stream))
  8. (aget :x-transmission-session-id headers)))
  9. (defun transmission-set-session (url session-id)
  10. (set-setting '*transmission-sessions*
  11. (cons (cons url session-id)
  12. (remove url *transmission-sessions* :test #'equal :key #'car))))
  13. (defun transmission-request (url method &rest arguments)
  14. (let ((session-id (aget url *transmission-sessions*))
  15. (content
  16. (with-output-to-string (stream)
  17. (yason:encode (alexandria:plist-hash-table
  18. (list "method" (dekeyify method t)
  19. "arguments" (alexandria:plist-hash-table
  20. (loop for (key value) on arguments by #'cddr
  21. when value
  22. appending (list (dekeyify key) value)))))
  23. stream))))
  24. (multiple-value-bind (stream status headers uri http-stream)
  25. (drakma:http-request url :method :post :content content :content-type "application/json"
  26. :force-binary t :want-stream t :decode-content t
  27. :additional-headers (list (cons :x-transmission-session-id session-id)))
  28. (declare (ignore uri))
  29. (ecase status
  30. (200 (unwind-protect
  31. (progn
  32. (setf (flex:flexi-stream-external-format stream) :utf-8)
  33. (let* ((response (yason:parse stream :object-as :alist))
  34. (result (aget "result" response)))
  35. (unless (equal "success" result)
  36. (error result))
  37. (aget "arguments" response)))
  38. (ignore-errors (close http-stream))))
  39. (409 (transmission-set-session url (aget :x-transmission-session-id headers))
  40. (apply #'transmission-request url method arguments))))))
  41. (defun transmission-get-torrents (url &optional ids (fields '("id" "name" "status" "percentDone" "eta" "totalSize")))
  42. (aget "torrents" (transmission-request url :torrent-get :ids ids :fields fields)))
  43. (defun transmission-add-torrent (url &key filename metainfo)
  44. (let ((torrent-added (transmission-request url :torrent-add :filename filename :metainfo metainfo)))
  45. (car (transmission-get-torrents url (list (aget "id" (aget "torrent-added" torrent-added)))))))
  46. (defun %format-torrent-status (status)
  47. (case status
  48. (0 "⏹") ;; Stopped
  49. (1 "🔂") ;; Check wait
  50. (2 "🔁") ;; Check
  51. (3 "⏸") ;; Queued to download
  52. (4 "▶️") ;; Downloading
  53. (5 "⏸") ;; Queued to seed
  54. (6 "⏺") ;; Seeding
  55. ))
  56. (defun %format-torrent (torrent)
  57. (let ((eta (aget "eta" torrent)))
  58. (format nil "~A ~A ~A (~A)~:[~; (~A%~@[, eta ~A~])~]"
  59. (aget "id" torrent)
  60. (%format-torrent-status (aget "status" torrent))
  61. (aget "name" torrent)
  62. (format-size (aget "totalSize" torrent))
  63. (eq 4 (aget "status" torrent))
  64. (smart-f (* 100 (aget "percentDone" torrent)) 1)
  65. (when (not (= -1 eta)) (format-interval eta)))))
  66. (def-message-cmd-handler handle-torrents (:torrents)
  67. (let ((url (aget chat-id *transmission-settings*)))
  68. (if url
  69. (bot-send-message chat-id
  70. (format nil "~{~A~^~%~}" (mapcar #'%format-torrent
  71. (transmission-get-torrents url))))
  72. (bot-send-message chat-id "Бота настрой!"))))
  73. (defparameter +magnet-regex+ (ppcre:create-scanner "magnet:\\?\\S+"))
  74. (def-message-handler handle-magnet (message)
  75. (alexandria:when-let* ((url (aget chat-id *transmission-settings*))
  76. (magnet (ppcre:scan-to-strings +magnet-regex+ text)))
  77. (bot-send-message chat-id (%format-torrent (transmission-add-torrent url :filename magnet)))))
  78. (def-message-handler handle-torrent (message)
  79. (alexandria:when-let* ((url (aget chat-id *transmission-settings*))
  80. (doc (aget "document" message))
  81. (file-name (aget "file_name" doc))
  82. (file-id (aget "file_id" doc)))
  83. (when (and (equal "torrent" (pathname-type (pathname file-name)))
  84. (< (aget "file_size" doc) (* 512 1024)))
  85. (let ((contents (telegram-file-content file-id)))
  86. (bot-send-message chat-id (%format-torrent (transmission-add-torrent url :metainfo (cl-base64:usb8-array-to-base64-string contents))))))))