summaryrefslogtreecommitdiffhomepage
path: root/attic
diff options
context:
space:
mode:
authorjao <jao@gnu.org>2021-08-17 22:10:57 +0100
committerjao <jao@gnu.org>2021-08-17 22:10:57 +0100
commit9a4b236b067b1abc3b1935e1f9587623a93500cd (patch)
treee95600f89391c84cff7b2dff602b7e0644825535 /attic
parent201fa34428a0029dd159906b1c36d9fc0ca55a71 (diff)
downloadelibs-9a4b236b067b1abc3b1935e1f9587623a93500cd.tar.gz
elibs-9a4b236b067b1abc3b1935e1f9587623a93500cd.tar.bz2
attic
Diffstat (limited to 'attic')
-rw-r--r--attic/misc/nnnm.el265
1 files changed, 265 insertions, 0 deletions
diff --git a/attic/misc/nnnm.el b/attic/misc/nnnm.el
new file mode 100644
index 0000000..552e95c
--- /dev/null
+++ b/attic/misc/nnnm.el
@@ -0,0 +1,265 @@
+;;; nnnm.el --- Gnus backend for notmuch -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 jao
+
+;; Author: jao <mail@jao.io>
+;; Keywords: mail
+
+;; 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 <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; A Gnus mail backend using notmuch.
+
+;;; Code:
+
+(require 'gnus)
+(require 'nnheader)
+(require 'nnmail)
+(require 'nnoo)
+
+
+(nnoo-declare nnnm)
+
+(defvar nnnm-marks-to-tags '((tick . "flagged")))
+
+(defvar nnnm-saved-searches nil)
+
+(defvar nnnm-maildir nil)
+
+(defvar nnnm--group-data nil)
+
+(defun nnnm--group-data (group) (cdr (assoc group nnnm--group-data)))
+
+(defun nnnm--set-group-data (group data)
+ (setf (alist-get group nnnm--group-data nil t #'string=) data))
+
+
+(defun nnnm--find-query (name)
+ (when-let (s (seq-find (lambda (s) (string= (plist-get s :name) name))
+ nnnm-saved-searches))
+ (plist-get s :query)))
+
+(defun nnnm--find-message-file (id)
+ (car (split-string
+ (shell-command-to-string
+ (format "notmuch search --output=files %s"
+ (if (string-prefix-p "id:" id) id (concat "id:" id)))))))
+
+(defun nnnm--article-data (article group)
+ (cond ((stringp article) (list article))
+ ((numberp article)
+ (when-let (data (nnnm--group-data group))
+ (elt data (1- article))))))
+
+(defun nnnm-article-to-file (article group)
+ (when-let (d (nnnm--article-data article group))
+ (or (cadr d) (nnnm--find-message-file (car d)))))
+
+(defun nnnm--count (query &optional context)
+ (let ((cmd (format "notmuch count -- '(%s)%s'"
+ query
+ (if context (concat " AND " context) ""))))
+ (string-to-number (shell-command-to-string cmd))))
+
+(defun nnnm--search (query &optional context)
+ (let ((cmd (format "notmuch search --sort=oldest-first --output=messages -- %s%s"
+ (shell-quote-argument (format "(%s)" query))
+ (if context (concat " AND " context) ""))))
+ (split-string (shell-command-to-string cmd))))
+
+(defun nnnm--tag (query tags)
+ (let ((cmd (format "notmuch tag %s -- '(%s)'" tags query)))
+ (shell-command-to-string cmd)))
+
+(defun nnnm--ids-query (ids) (mapconcat #'identity ids " OR "))
+
+(defun nnnm--prefixed (group server)
+ (gnus-group-prefixed-name group `(nnnm ,server)))
+
+(defun nnnm--get-group-marks (group server)
+ (gnus-info-marks (gnus-get-info (nnnm--prefixed group server))))
+
+(defun nnnm--set-group-marks (marks group server)
+ (let* ((full-group (nnnm--prefixed group server))
+ (info (gnus-get-info full-group)))
+ (gnus-info-set-marks info marks)
+ (gnus-set-info full-group info)))
+
+(defun nnnm--subtract-from-ranges (ranges lst)
+ (let ((ranges (gnus-uncompress-sequence ranges)))
+ (dolist (n lst)
+ (let ((rs (seq-group-by (lambda (r) (> n r)) ranges)))
+ (setq ranges
+ (append (alist-get t rs) (mapcar #'1- (alist-get nil rs))))))
+ (gnus-compress-sequence ranges)))
+
+(defun nnnm--remove-articles-from-marks (ranges group server)
+ (let ((marks (nnnm--get-group-marks group server))
+ (lst (gnus-uncompress-sequence ranges))
+ (new-marks))
+ (dolist (m marks)
+ (push (cons (car m) (nnnm--subtract-from-ranges (cdr m))) lst))
+ (nnnm--set-group-marks marks group server)))
+
+(defun nnnm--set-active (n group server)
+ (gnus-set-active (nnnm--prefixed group server) (cons 1 n)))
+
+(defun nnnm--update-group-data (group &optional server)
+ (when-let (query (nnnm--find-query group))
+ (let* ((data (or (nnnm--group-data group)
+ (mapcar #'list (nnnm--search query "NOT tag:new"))))
+ (ids (nnnm--search query "tag:new"))
+ (nids (length ids))
+ (new-data (mapcar (lambda (id)
+ (list id (nnnm--find-message-file id)))
+ ids)))
+ (when (> nids 0)
+ (nnnm--tag (nnnm--ids-query ids) "-new")
+ (nnheader-report 'nnnm "%s: %d new messages retrieved" group nids))
+ (nnnm--set-group-data group (append data new-data))
+ (nnnm--set-active (+ nids (length data)) group server)
+ (length ids))))
+
+
+
+;;; Interface functions.
+
+(nnoo-define-basics nnnm)
+
+(defun nnnm-request-type (_group &optional _article)
+ 'mail)
+
+(deffoo nnnm-open-server (server &optional defs)
+ (nnoo-change-server 'nnnm server defs)
+ (setq nnnm--group-data nil)
+ (nnheader-report 'nnnm "Opened server %s" server)
+ t)
+
+(deffoo nnnm-close-server (_server)
+ (setq nnnm--group-data nil))
+
+(deffoo nnnm-request-regenerate (_server)
+ (setq nnnm--group-data nil)
+ t)
+
+(deffoo nnnm-request-list (&optional _server)
+ (with-current-buffer nntp-server-buffer
+ (erase-buffer)
+ (dolist (s nnnm-saved-searches)
+ (when-let (query (plist-get s :query))
+ (let ((name (plist-get s :name))
+ (total (nnnm--count query)))
+ (insert (format "%s %d 1 y\n" name total))))))
+ t)
+
+(deffoo nnnm-retrieve-headers (sequence &optional group server _fetch-old)
+ (when (nnnm--update-group-data group server)
+ (with-current-buffer nntp-server-buffer
+ (delete-region (point-min) (point-max))
+ (let ((file-name-coding-system nnmail-pathname-coding-system)
+ (count -1))
+ (if (stringp (car sequence))
+ 'headers
+ (dolist (article sequence)
+ (when-let (file (nnnm-article-to-file article group))
+ (insert (format "221 %d Article retrieved.\n" article))
+ (save-excursion (nnheader-insert-head file))
+ (if (re-search-forward "\n\r?\n" nil t)
+ (forward-char -1)
+ (goto-char (point-max))
+ (insert "\n\n"))
+ (insert ".\n")
+ (delete-region (point) (point-max)))
+ (when (zerop (% (cl-incf count) 20))
+ (nnheader-message 6 "nnnm: Receiving headers... %d%%"
+ (floor (* count 100.0) (length sequence)))))
+ (nnheader-fold-continuation-lines)
+ 'headers)))))
+
+(deffoo nnnm-request-article (id &optional group _server buffer)
+ (let* ((nntp-server-buffer (or buffer nntp-server-buffer))
+ (file-name-coding-system nnmail-pathname-coding-system)
+ (d (nnnm--article-data id group))
+ (id (car d))
+ (file (when id (or (cadr d) (nnnm--find-message-file id)))))
+ (cond
+ ((not file)
+ (nnheader-report 'nnnm "No such article: %s" id))
+ ((not (file-exists-p file))
+ (nnheader-report 'nnnm "No such file: %s" file))
+ ((not (save-excursion (nnmail-find-file file)))
+ (nnheader-report 'nnnm "Couldn't read file: %s" file))
+ (t
+ (nnnm--tag id "-unread")
+ (nnheader-report 'nnnm "Article %s retrieved and tagged" id)
+ (cons group id)))))
+
+(deffoo nnnm-request-expire-articles (articles group &optional _server _force)
+ (let* ((articles (gnus-uncompress-range articles))
+ (ids (mapcar (lambda (a) (car (nnnm--article-data a group))) articles)))
+ (when ids
+ (nnnm--tag (nnnm--ids-query ids) "+deleted")
+ (let ((data (nnnm--group-data group)))
+ (dolist (id ids)
+ (setq data
+ (cl-delete-if (lambda (d) (string= (car d) id)) data :count 1)))
+ (nnnm--set-group-data group data)
+ (nnnm--remove-articles-from-marks articles group server)
+ (nnnm--set-active (length data) group server)))
+ articles))
+
+(deffoo nnnm-request-set-mark (group actions &optional _server)
+ (message "set marks: %s: %S" group actions)
+ actions)
+
+;; (deffoo nnnm-request-move-article
+;; (article group server accept-form &optional last _move-is-internal)
+;; (error "Not implemented yet"))
+
+(deffoo nnnm-request-group (group &optional server _dont-check info)
+ (nnheader-message 7 "nnnm: Opening %s -- %s" info group)
+ (if (nnnm--update-group-data group server)
+ (let ((n (length (nnnm--group-data group))))
+ (with-current-buffer nntp-server-buffer
+ (erase-buffer)
+ (nnheader-insert "211 %d %d %d %s\n" n 1 n group)
+ n))
+ (nnheader-report 'nnnm "Invalid group")))
+
+(deffoo nnnm-request-newgroups (_date &optional server)
+ (nnnm-request-list server))
+
+(deffoo nnnm-request-group-scan (group &optional server _info)
+ (nnnm--set-group-data group nil)
+ (nnnm--update-group-data group server))
+
+(deffoo nnnm-request-scan (&optional group server)
+ (if group
+ (nnnm--update-group-data group server)
+ (setq nnnm--group-data nil)))
+
+(deffoo nnnm-request-create-group (group &optional _server _args)
+ (let ((query (read-string "Query: ")))
+ (add-to-list 'nnnm-saved-searches `(:name ,group :query ,query))))
+
+;; (deffoo nnnm-request-rename-group (group new-name &optional _server)
+;; (error "Not implemented yet"))
+
+(deffoo nnnm-close-group (_group &optional _server) t)
+
+
+
+(provide 'nnnm)
+;;; nnnm.el ends here