Browse code
saving progress and ignoring build artifacts
fiddlerwoaroof authored on 23/05/2016 23:34:08
Showing 5 changed files
Showing 5 changed files
... | ... |
@@ -5,6 +5,33 @@ |
5 | 5 |
;;; "simple_list" goes here. Hacks and glory await! |
6 | 6 |
|
7 | 7 |
(defvar *imap-socket*) |
8 |
+(defvar *mailing-list*) |
|
9 |
+ |
|
10 |
+(defclass <mailing-list> () |
|
11 |
+ ((name :initform nil :initarg :name :accessor ml-name) |
|
12 |
+ (emails :initform nil :initarg :name :accessor ml-emails))) |
|
13 |
+ |
|
14 |
+(manardb:defmmclass <persistent-mailing-list> () |
|
15 |
+ ((name :initform nil :initarg :name :accessor ml-name) |
|
16 |
+ (emails :initform nil :initarg :emails :accessor ml-emails)) ) |
|
17 |
+ |
|
18 |
+(defmethod print-object ((obj <persistent-mailing-list>) s) |
|
19 |
+ (print-unreadable-object (obj s :type t :identity t) |
|
20 |
+ (format s "~a with ~d emails" (ml-name obj) (length (ml-emails obj))))) |
|
21 |
+ |
|
22 |
+(defun make-mailing-list (name &optional persistent) |
|
23 |
+ (aprog1 (make-instance '<persistent-mailing-list>) |
|
24 |
+ (setf (slot-value it 'name) |
|
25 |
+ (make-keyword |
|
26 |
+ (ctypecase name |
|
27 |
+ (string (string-to-list-name name)) |
|
28 |
+ (symbol name)))))) |
|
29 |
+ |
|
30 |
+(defmacro aprogn (&body body) |
|
31 |
+ `(let* |
|
32 |
+ ,(loop for el in body |
|
33 |
+ collect `(it ,el)) |
|
34 |
+ it)) |
|
8 | 35 |
|
9 | 36 |
(defmacro define-special-variable-wrapper (name var &body value-form) |
10 | 37 |
(alexandria:with-gensyms (body) |
... | ... |
@@ -13,48 +40,55 @@ |
13 | 40 |
`(let ((,var ,@value-form)) |
14 | 41 |
,@,body))))) |
15 | 42 |
|
16 |
-(defmacro aprogn (&body body) |
|
17 |
- `(let* |
|
18 |
- ,(loop for el in body |
|
19 |
- collect `(it ,el)) |
|
20 |
- it)) |
|
43 |
+(eval-when (:compile-toplevel :load-toplevel :execute) |
|
44 |
+ (defun get-connection-spec () |
|
45 |
+ (clonsigna:make-imap :host (ubiquitous:value 'imap 'host) |
|
46 |
+ :port (ubiquitous:value 'imap 'port) |
|
47 |
+ :ssl-p (ubiquitous:value 'imap 'ssl-p)))) |
|
21 | 48 |
|
22 |
-(defun get-connection-spec () |
|
23 |
- (clonsigna:make-imap :host (ubiquitous:value 'imap 'host) |
|
24 |
- :port (ubiquitous:value 'imap 'port) |
|
25 |
- :ssl-p (ubiquitous:value 'imap 'ssl-p))) |
|
26 | 49 |
|
27 | 50 |
(define-special-variable-wrapper with-connection *imap-socket* (get-connection-spec)) |
51 |
+(define-special-variable-wrapper with-mailing-lists *mailing-list* |
|
52 |
+ ((manardb:retrieve-all-instances '<persistent-mailing-list>)) |
|
53 |
+ ) |
|
28 | 54 |
|
29 |
-(defun connect-toplevel () |
|
30 |
- (setf *imap-socket* (get-connection-spec))) |
|
55 |
+(defmacro insert-imap-socket (&body body) |
|
56 |
+ (list* 'progn |
|
57 |
+ (loop for cmd in body |
|
58 |
+ collect (list* (car cmd) '*imap-socket* (cdr cmd))))) |
|
31 | 59 |
|
32 | 60 |
(defun connect-and-authorize (auth-info) |
33 | 61 |
(clonsigna:cmd-connect *imap-socket*) |
34 | 62 |
(clonsigna:cmd-login *imap-socket* (car auth-info) (cdr auth-info))) |
35 | 63 |
|
64 |
+(defmacro with-authorized-connection ((auth-info) &body body) |
|
65 |
+ `(with-connection () |
|
66 |
+ (connect-and-authorize ,auth-info) |
|
67 |
+ ,@body)) |
|
68 |
+ |
|
69 |
+(defun connect-toplevel () |
|
70 |
+ (setf *imap-socket* (get-connection-spec))) |
|
36 | 71 |
|
37 | 72 |
(defun get-mailbox (name) |
38 | 73 |
(cmd-select *imap-socket* name) |
39 | 74 |
(mapcar #'princ-to-string |
40 |
- (alexandria:flatten |
|
41 |
- (parse-thread (cmd-thread *imap-socket* :uid-p t))))) |
|
75 |
+ (parse-search (cmd-search *imap-socket* :criteria "(recent unseen)" :uid-p t)))) |
|
42 | 76 |
|
43 |
-(defun get-inbox () |
|
44 |
- (get-mailbox "inbox")) |
|
77 |
+(defun get-inbox () (get-mailbox "inbox")) |
|
45 | 78 |
|
46 | 79 |
(defun get-headers (uid) |
47 | 80 |
(parse-fetch-fields (cmd-fetch-fields *imap-socket* uid))) |
48 | 81 |
|
49 | 82 |
(defun get-bodystructures (uid) |
50 |
- (let ((headers (get-headers x))) |
|
83 |
+ (let ((headers (get-headers uid))) |
|
51 | 84 |
(mapcar (lambda (x) |
52 | 85 |
(make-bodystructure |
53 | 86 |
(parse-bodystructure |
54 |
- (getf x :bodystructure))))))) |
|
87 |
+ (getf x :bodystructure)))) |
|
88 |
+ headers))) |
|
55 | 89 |
|
56 | 90 |
(defun get-raw-message (uid) |
57 |
- (let* ((message (split-sequence #\Newline (car (cmd-fetch-body uid)))) |
|
91 |
+ (let* ((message (split-sequence #\Newline (car (cmd-fetch-body *imap-socket* uid :uid-p t)))) |
|
58 | 92 |
(message-without-imap (slice message 0 -1))) |
59 | 93 |
(string-join message-without-imap #\Newline))) |
60 | 94 |
|
... | ... |
@@ -62,14 +96,13 @@ |
62 | 96 |
(let* ((bodystructures (get-bodystructures uid)) |
63 | 97 |
(bodies (flatten (mapcar #'bodystructure-body-list bodystructures))) |
64 | 98 |
(sections (flatten (mapcar #'structure-element-section bodies))) |
65 |
- (charsets (flatten (mapcar (fw.lu::alambda |
|
99 |
+ (charsets (flatten (mapcar (fw.lu:alambda |
|
66 | 100 |
(cadr (structure-element-body-parameters it))) |
67 | 101 |
bodies)))) |
68 | 102 |
(parse-fetch-body |
69 | 103 |
(car (cmd-fetch-body *imap-socket* uid :section sections :uid-p t)) |
70 | 104 |
:charset (car charsets)))) |
71 | 105 |
|
72 |
-(defvar *mailing-list*) |
|
73 | 106 |
|
74 | 107 |
(defun lookup-list (list-name &optional (lookup-table *mailing-list*)) |
75 | 108 |
(cdr (assoc list-name lookup-table))) |
... | ... |
@@ -112,12 +145,15 @@ |
112 | 145 |
headers ; Message headers |
113 | 146 |
envelope))) ; Envelope info |
114 | 147 |
|
148 |
+(defun string-to-list-name (name-string) |
|
149 |
+ (aprogn name-string |
|
150 |
+ (string-upcase it) |
|
151 |
+ (make-keyword it))) |
|
152 |
+ |
|
115 | 153 |
(defun process-imap-message (uid host auth-info) |
116 | 154 |
(multiple-value-bind (to from subject flags) (get-message-envelope-info uid) |
117 | 155 |
(unless (member 'seen flags) |
118 |
- (let* ((list-name (aprogn (email-to-list to) |
|
119 |
- (string-upcase it) |
|
120 |
- (make-keyword it))) |
|
156 |
+ (let* ((list-name (string-to-list-name (email-to-list to))) |
|
121 | 157 |
(body (get-message uid))) |
122 | 158 |
(send-to-list list-name from subject body host auth-info))))) |
123 | 159 |
|
... | ... |
@@ -128,15 +164,48 @@ |
128 | 164 |
(defun process-inbox (host auth-info) |
129 | 165 |
(process-imap-mailbox "inbox" host auth-info)) |
130 | 166 |
|
131 |
-(define-special-variable-wrapper with-mailing-lists *mailing-list* (ubiquitous:value 'mailing-lists)) |
|
132 |
- |
|
133 |
-(defun main () |
|
167 |
+(defun get-auth-info () |
|
168 |
+ (cons (ubiquitous:value 'auth 'user) |
|
169 |
+ (ubiquitous:value 'auth 'password))) |
|
170 |
+ |
|
171 |
+(define-condition simple-list-error () () |
|
172 |
+ (:documentation "The base condition for exceptions thrown by this utility")) |
|
173 |
+ |
|
174 |
+(define-condition no-such-list (simple-list-error) |
|
175 |
+ ((list-name :initarg :name :accessor ml-name)) |
|
176 |
+ (:report (lambda (c s) (format s "No list named ~a" (ml-name c))))) |
|
177 |
+ |
|
178 |
+(defgeneric add-email (mailing-list email) |
|
179 |
+ (:method :around (mailing-list email) |
|
180 |
+ (let ((result (call-next-method))) |
|
181 |
+ (typecase mailing-list |
|
182 |
+ (symbol result) |
|
183 |
+ (t mailing-list)))) |
|
184 |
+ |
|
185 |
+ (:method ((mailing-list <persistent-mailing-list>) email) |
|
186 |
+ (with-slots (emails) mailing-list |
|
187 |
+ (unless (member email emails :test #'equalp) |
|
188 |
+ (push email emails)))) |
|
189 |
+ |
|
190 |
+ (:method ((list-name symbol) email) |
|
191 |
+ (let ((lists (remove-if-not |
|
192 |
+ (fw.lu:alambda (eq list-name (ml-name it))) |
|
193 |
+ (manardb:retrieve-all-instances '<persistent-mailing-list>)))) |
|
194 |
+ (if-let ((mailing-list (car lists))) |
|
195 |
+ (add-email mailing-list email) |
|
196 |
+ (error 'no-such-list :name list-name))))) |
|
197 |
+ |
|
198 |
+(defun add-email-to-list (list-name email) |
|
199 |
+ (restart-case |
|
200 |
+ (add-email list-name email) |
|
201 |
+ (make-list () (add-email (make-mailing-list list-name) email)))) |
|
202 |
+ |
|
203 |
+(defun main (&optional argv) |
|
134 | 204 |
(ubiquitous:restore 'simple_list) |
135 |
- |
|
136 |
- (let ((auth-info (cons (ubiquitous:value 'auth 'user) |
|
137 |
- (ubiquitous:value 'auth 'password)))) |
|
138 |
- (with-connection () |
|
139 |
- (with-mailing-lists () |
|
140 |
- (connect-and-authorize auth-info) |
|
141 |
- (get-inbox) |
|
142 |
- (process-inbox (ubiquitous:value 'imap 'host) auth-info))))) |
|
205 |
+ (let ((auth-info (get-auth-info)) |
|
206 |
+ (host-name (ubiquitous:value 'imap 'host))) |
|
207 |
+ (with-mailing-lists () |
|
208 |
+ (format t "Connecting to server ~a" host-name) |
|
209 |
+ (with-authorized-connection (auth-info) |
|
210 |
+ (setf +debug+ t) |
|
211 |
+ (process-inbox host-name auth-info))))) |