-
Notifications
You must be signed in to change notification settings - Fork 0
/
org-msg-merge.el
140 lines (114 loc) · 4.7 KB
/
org-msg-merge.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
;;; org-msg-merge.el --- Extension of org-msg to send bulk email messages. -*- lexical-binding: t; -*-
;; Copyright (C) 2021 Hermann Graf von Westerholt
;; Author: Hermann Graf von Westerholt <[email protected]>
;; Created: November 2021
;; Keywords: extensions 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; None so far.
;;; Code:
(require 'org-msg)
(require 'pcsv)
(defcustom org-msg-merge-send-immediately nil
"Whether created email is sent immediately.
If this is non-nil, then `message-send-and-exit' will be
called after the email has been composed."
:type 'boolean)
(defun org-msg-merge-create-send-maybe (template alist send)
"Create mail from TEMPLATE with headers and replacers from LST.
TEMPLATE can be a string or a file path.
LST is a list of alists of the form:
(((TO . [email protected])
(SUBJECT . subject1)
(replace-this . text1)
(and-this . moretext1))
((TO . [email protected])
(SUBJECT . subject2)
(replace-this . text2)
(and-this . moretext2))
...)"
;; if template is readable file, use its contents
(let* ((template (if (file-readable-p template)
(org-msg-merge-file-content-as-string template)
template))
;; list of attachments
(attachments )
;; disable greeting
(org-msg-greeting-fmt nil))
(dolist (recipient alist)
;; insert header information
(compose-mail (cdr (assoc "TO" recipient))
(cdr (assoc "SUBJECT" recipient))
(list (cons "CC" (cdr (assoc "CC" recipient)))
(cons "BCC" (cdr (assoc "BCC" recipient))))
nil)
;; insert body of email with replacers
(org-msg-goto-body)
(insert (concat (s-format template 'aget recipient) "\n"))
;; take care of attachments
(let ((attachments (mapcar 'cdr
(seq-filter
(lambda(x) (s-matches? "^ATTACHMENT" (car x)))
recipient))))
(org-msg-set-prop "attachment" attachments))
;; keep buffer open in background or send immediately
(when send
(message-send-and-exit)))))
(defun org-msg-merge-from-csv (csv template &optional send)
"Use information in CSV to fill placeholders in TEMPLATE.
If SEND is non-nil, send emails immediately."
(interactive "fChoose CSV file: \nfChoose template file: ")
(let* ((alist (org-msg-merge-convert-tbl-to-alist (pcsv-parse-file csv)))
;; determine if mail(s) should be sent immediately or not
(send (or send org-msg-merge-send-immediately)))
(org-msg-merge-create-send-maybe template alist send)))
(defun org-msg-merge-from-org-tbl ()
".")
(defun org-msg-merge-convert-tbl-to-alist (tbl)
"Convert rows of TBL into list of alists for easier iteration.
Example:
Input: ((\"header1\" \"header2\" \"header3\")
(\"lin1col1\" \"lin1col2\" \"lin1col3\")
(\"lin2col1\" \"lin2col2\" \"lin2col3\")
(\"lin3col1\" \"lin3col2\" \"lin3col3\"))
Output: (((\"header1\" . \"lin1col1\")
(\"header2\" . \"lin1col2\")
(\"header3\" . \"lin1col3\"))
((\"header1\" . \"lin2col1\")
(\"header2\" . \"lin2col2\")
(\"header3\" . \"lin2col3\"))
((\"header1\" . \"lin3col1\")
(\"header2\" . \"lin3col2\")
(\"header3\" . \"lin3col3\")))"
(let* ((headers (car tbl))
(rows (cdr tbl)))
(mapcar (lambda(row)
(org-msg-merge-mapcar* 'cons headers row))
rows)))
(defun org-msg-merge-mapcar* (function &rest args)
"Apply FUNCTION to successive cars of all ARGS.
Return the list of results. This function has been
stolen from section 13.6 of the Elisp manual."
;; If no list is exhausted,
(if (not (memq nil args))
;; apply function to CARs.
(cons (apply function (mapcar 'car args))
(apply 'mapcar* function
;; Recurse for rest of elements.
(mapcar 'cdr args)))))
(defun org-msg-merge-file-content-as-string (filename)
"Return the contents of FILENAME as string."
(with-temp-buffer
(insert-file-contents filename)
(buffer-string)))
(provide 'org-msg-merge)
;;; org-msg-merge.el ends here