git.fiddlerwoaroof.com
Browse code

Add postmodern store, make references more reliable

Ed L authored on 29/06/2019 22:42:31
Showing 9 changed files
... ...
@@ -7,6 +7,17 @@
7 7
 
8 8
 (cl:in-package :hhgbot-2)
9 9
 
10
+(defun normalize-ref (value)
11
+  (when (or (alexandria:ends-with-subseq "co" value)
12
+            (alexandria:ends-with-subseq "s.c" value))
13
+    (setf value (format nil "~a." value)))
14
+  (when (alexandria:ends-with-subseq "sc" value)
15
+    (setf value (format nil "~as.c." (subseq value 0 (- (length value) 2)))))
16
+  (when (alexandria:ends-with-subseq "sc." value)
17
+    (setf value (format nil "~as.c." (subseq value 0 (- (length value) 3)))))
18
+
19
+  value)
20
+
10 21
 ;; Special Variables
11 22
 
12 23
 (defvar *xxx* (make-synonym-stream '*standard-output*))
... ...
@@ -21,9 +32,10 @@
21 32
    (%conversations :initform (make-hash-table :test 'equal))))
22 33
 
23 34
 
24
-(defclass hhgbot-event-pump (slacker:event-pump slacker.montezuma-store:montezuma-store logging-slackbot)
35
+(defclass hhgbot-event-pump (slacker:event-pump #+(or)slacker.montezuma-store:montezuma-store logging-slackbot
36
+			     slacker.postmodern-store:postmodern-store)
25 37
   ()
26
-  (:default-initargs :index-path "/tmp/slack-idx/"))
38
+  #+(or)(:default-initargs :index-path "/tmp/slack-idx/"))
27 39
 
28 40
 (defun start-in-repl (&optional (start-bot t) (team-id :atomampd))
29 41
   (ubiquitous:restore :hhgbot-augmented-assistant)
... ...
@@ -44,7 +56,8 @@
44 56
       (let ((value (read-line)))
45 57
         (setf (ubiquitous:value :api-token team-id) value
46 58
               slacker::*api-token* value)))
47
-    (values (slacker:coordinate-threads *queue-pair* 'hhgbot-event-pump)
59
+    (values (slacker:coordinate-threads *queue-pair* 'hhgbot-event-pump
60
+					'(:postgres-connection-spec ("edwlan" "edwlan" nil :unix)))
48 61
             slacker::*api-token*)))
49 62
 
50 63
 (defun ensure-unescaped (src)
... ...
@@ -90,10 +103,13 @@
90 103
 
91 104
 (define-command "ref>" (event-pump message channel &optional source ref &rest args)
92 105
   (declare (ignore args))
106
+  (when args
107
+    (setf ref (format nil "~a~{~a~}" ref args)))
108
+
93 109
   (if (and source ref)
94 110
       (when-let ((source-h (gethash source *refs*)))
95 111
         (queue-message event-pump channel
96
-                       (gethash ref source-h (concat "Can't find " source " " ref))
112
+                       (gethash (normalize-ref ref) source-h (concat "Can't find " source " " ref))
97 113
                        :thread (keep-in-thread message)))
98 114
       (queue-message event-pump channel (concat "Must provide both a source and a reference. See ;sources")
99 115
                      :thread (keep-in-thread message))))
... ...
@@ -130,27 +146,30 @@
130 146
   (values-list args))
131 147
 
132 148
 (define-message-command "arc" (event-pump message channel &rest args)
133
-  (let ((r (with-output-to-string (s)
134
-	     (multiple-value-bind (results idx)
135
-		 (slacker.montezuma-store:search-index *client* "message" (string-join args " "))
136
-	       (montezuma:each results
137
-			       (lambda (h)
138
-				 (format s "> ~a: ~a~%"
139
-					 (local-time:format-timestring
140
-					  nil
141
-					  (local-time:unix-to-timestamp
142
-					   (floor 
143
-					    (parse-number
149
+  (if (equal (gethash "user" message)
150
+	     "U0CSPP3SB")
151
+      (let ((r (with-output-to-string (s)
152
+		 (multiple-value-bind (results idx)
153
+		     (slacker.montezuma-store:search-index *client* "message" (string-join args " "))
154
+		   (montezuma:each results
155
+				   (lambda (h)
156
+				     (format s "> ~a: ~a~%"
157
+					     (local-time:format-timestring
158
+					      nil
159
+					      (local-time:unix-to-timestamp
160
+					       (floor 
161
+						(parse-number
162
+						 (montezuma:document-value (montezuma:get-document idx (montezuma:doc h))
163
+									   "ts")))
164
+					       )
165
+					      :format local-time:+rfc3339-format+)
166
+					     
144 167
 					     (montezuma:document-value (montezuma:get-document idx (montezuma:doc h))
145
-								       "ts")))
146
-					   )
147
-					  :format local-time:+rfc3339-format+)
148
-					 
149
-					 (montezuma:document-value (montezuma:get-document idx (montezuma:doc h))
150
-								   "text"))))))))
151
-    (if (= 0 (length r))
152
-        (format nil "No results found for: `~a`" (string-join args " "))
153
-	r)))
168
+								       "text"))))))))
169
+	(if (= 0 (length r))
170
+	    (format nil "No results found for: `~a`" (string-join args " "))
171
+	    r))
172
+      (format nil "No results found for: `~a`" (string-join args " "))))
154 173
 
155 174
 (defun extract-channel-info (channels)
156 175
   (funcall (data-lens:pick
... ...
@@ -266,7 +285,9 @@ Return a string with the generated JSON output."
266 285
 (define-controller quote (params)
267 286
   (let* ((text (cdr (assoc :text params)))
268 287
          (ref (cdr (assoc :ref params))))
269
-    (cons (cons text ref) (gethash ref (gethash text hhgbot-2::*refs*)))))
288
+    (cons (cons text ref)
289
+	  (gethash (hhgbot-2::normalize-ref ref)
290
+		   (gethash text hhgbot-2::*refs*)))))
270 291
 
271 292
 (define-controller random-quote (params)
272 293
   (declare (ignore params))
... ...
@@ -281,15 +302,15 @@ Return a string with the generated JSON output."
281 302
 (define-view random-quote (model)
282 303
   (destructuring-bind ((book . ref) . text) model
283 304
     `(302
284
-      ("Location"  ,(format nil "http://hhgbot.edw.ai:5000/q/~a/~a"
305
+      ("Location"  ,(format nil "/q/~a/~a"
285 306
                             book ref))
286 307
       (,text))))
287 308
 
288 309
 (define-spinneret-view quote (quote)
289 310
   (let ((title (format nil "Quote: ~a, ~a" (caar quote) (cdar quote)))
290
-        (permalink (format nil "http://hhgbot.edw.ai:5000/q/~a/~a"
311
+        (permalink (format nil "/q/~a/~a"
291 312
                            (caar quote)
292
-                           (cdar quote))))
313
+                           (hhgbot-2::normalize-ref (cdar quote)))))
293 314
     (:html
294 315
      (:head (:title title)
295 316
             (:link :href "https://fonts.googleapis.com/css?family=Lato:400&subset=latin,latin-ext" :rel "stylesheet" :type "text/css")
... ...
@@ -2,30 +2,45 @@
2 2
   (:use :cl :alexandria :serapeum :fw.lu)
3 3
   (:export
4 4
    #:montezuma-store
5
-   #:search-index))
5
+   #:search-index
6
+   #:retire-open-indices))
6 7
 (in-package :slacker.montezuma-store)
7 8
 
8 9
 (defclass montezuma-store ()
9
-  ((%indexes :reader indexes :initform (make-hash-table :test #'equal))
10
-   (%montezuma-index-path :reader index-path :initarg :index-path))
10
+  ((%indexes :reader indexes
11
+	     :initform (make-hash-table :test #'equal :synchronized t))
12
+   (%montezuma-index-path :reader index-path
13
+			  :initarg :index-path))
11 14
   (:default-initargs :index-path nil))
12 15
 
16
+(defun retire-open-indices (client)
17
+  (sb-ext:with-locked-hash-table ((indexes client))
18
+    (let ((old-indexes (indexes client)))
19
+      (setf (slot-value client '%indexes) 
20
+	    (make-hash-table :test #'equal :synchronized t))
21
+      (alexandria:maphash-values (lambda (v) (montezuma:close v)) 
22
+				 old-indexes))))
23
+
13 24
 (defun search-index (store index text)
14
-  (let* ((indexes (indexes store))
15
-	 (index (gethash index indexes)))
16
-    (values (montezuma:search index (format nil "!text:\";arc\" text:~a" text) :num-docs 3)
17
-	    index)))
25
+  (let* ((index (ensure-index-for-type store index)))
26
+    (values
27
+     (montezuma:search index
28
+		       (format nil "!text:\";arc\" text:~a" text)
29
+		       :num-docs 3)
30
+     index)))
18 31
 
19 32
 (defun ensure-index-for-type (store type)
20
-  (ensure-gethash type (indexes store)
21
-                  (if (index-path store)
22
-                      (make-instance 'montezuma:index :path (ensure-directories-exist
23
-                                                             (format nil "~a/~a/"
24
-                                                                     (index-path store)
25
-                                                                     type))
26
-                                                      :create-p nil
27
-                                                      :create-if-missing-p t)
28
-                      (make-instance 'montezuma:index))))
33
+  (sb-ext:with-locked-hash-table ((indexes store))
34
+    (ensure-gethash type (indexes store)
35
+		   (if (index-path store)
36
+		       (make-instance 'montezuma:index
37
+				      :path (ensure-directories-exist
38
+					     (format nil "~a/~a/"
39
+						     (index-path store)
40
+						     type))
41
+				      :create-p nil
42
+				      :create-if-missing-p t)
43
+		       (make-instance 'montezuma:index)))))
29 44
 
30 45
 (defgeneric combine-child (parent key value)
31 46
   (:method (parent k child)
... ...
@@ -51,15 +66,16 @@
51 66
     (do-hash-table (k v hash-table new)
52 67
       (combine-child new k v))))
53 68
 
54
-
55 69
 (defgeneric store-message (store message)
56 70
   (:method ((store montezuma-store) message)
57 71
     (let* ((type (gethash "type" message))
58 72
            (index (ensure-index-for-type store type)))
59
-      (montezuma:add-document-to-index index (flatten-hash-table message))
73
+      (montezuma:add-document-to-index index
74
+				       (flatten-hash-table message))
60 75
       (montezuma:flush index))))
61 76
 
62
-(defmethod slacker:handle-message :before (type (event-pump montezuma-store) ts channel message)
77
+(defmethod slacker:handle-message :before
78
+    (type (event-pump montezuma-store) ts channel message)
63 79
   (declare (ignore type ts channel))
64 80
   (store-message event-pump message)
65 81
   (values))
66 82
new file mode 100644
... ...
@@ -0,0 +1,25 @@
1
+(defpackage :slacker.postmodern-store
2
+  (:use :cl :alexandria :serapeum :fw.lu)
3
+  (:export #:postmodern-store))
4
+(in-package :slacker.postmodern-store)
5
+
6
+(defclass postmodern-store ()
7
+  ((%connection :reader connection)
8
+   (%connection-spec :reader connection-spec :initarg :postgres-connection-spec)))
9
+
10
+(defmethod shared-initialize :after ((instance postmodern-store) slot-names &key postgres-connection-spec)
11
+  (setf (slot-value instance '%connection)
12
+	(apply 'postmodern:connect postgres-connection-spec)))
13
+
14
+(defun save-slack-message (value)
15
+  (pomo:query "INSERT INTO slack_messages_raw (message) VALUES ($1) RETURNING id,slack_ts,message_text;"
16
+              value :rows))
17
+
18
+(defmethod slacker:handle-message :before
19
+    (type (event-pump postmodern-store) ts channel message)
20
+  (declare (ignore type ts channel))
21
+  (let ((pomo:*database* (connection event-pump)))
22
+    (save-slack-message
23
+     (with-output-to-string (s)
24
+       (yason:encode message s))))
25
+  (values))
0 26
new file mode 100644
... ...
@@ -0,0 +1,8 @@
1
+(defpackage :slacker.postgres-store
2
+  (:use :cl :alexandria :serapeum :fw.lu)
3
+  (:export #:postgres-store))
4
+(in-package :slacker.postgres-store)
5
+
6
+(defclass postgres-store ()
7
+  ((%connection-string :reader connection-string :initarg :postgres-connection-string))
8
+  (:default-initargs :index-path nil))
... ...
@@ -105,10 +105,11 @@
105 105
 
106 106
   (bt:make-thread (op (fwoar.event-loop:run-loop event-pump))))
107 107
 
108
-(defun start-client (implementation &key (queue-pair (make-instance 'queue-pair)) modules)
109
-  (let* ((event-pump (make-instance implementation
110
-                                    :queue-pair queue-pair
111
-                                    :client-factory (op (make-client _)))))
108
+(defun start-client (implementation &key (queue-pair (make-instance 'queue-pair)) modules impl-args)
109
+  (let* ((event-pump (apply #'make-instance implementation
110
+			    :queue-pair queue-pair
111
+			    :client-factory (op (make-client _))
112
+			    impl-args)))
112 113
     (setf (running event-pump) t)
113 114
     (values event-pump
114 115
             (bt:make-thread (lambda ()
... ...
@@ -186,10 +187,11 @@
186 187
               (reply )))
187 188
     do (sleep 0.01)))
188 189
 
189
-(defun coordinate-threads (&optional queue-pair (implementation 'event-pump))
190
+(defun coordinate-threads (&optional queue-pair (implementation 'event-pump) args)
190 191
   (let* ((event-pump (start-client implementation
191 192
                                    :queue-pair queue-pair
192
-                                   :modules '((hhgbot-augmented-assistant::js-executor)))))
193
+                                   :modules '((hhgbot-augmented-assistant::js-executor))
194
+				   :impl-args args)))
193 195
     (bt:make-thread (lambda () (loop until (running event-pump)
194 196
                                      finally (event-loop event-pump)))
195 197
                     :name "Event Loop") 
... ...
@@ -2,9 +2,11 @@
2 2
 (in-package :asdf-user)
3 3
 
4 4
 (asdf:defsystem #:slacker
5
-  :description "Describe hhgbot here"
6
-  :author "Your Name <your.name@example.com>"
7
-  :license "Specify license here"
5
+  :description #.(concatenate 'string
6
+			      "A Client for Slack's RTM API"
7
+			      " and wrappers for its REST API")
8
+  :author "Ed L <edward@elangley.org>"
9
+  :license "Apache/v2"
8 10
   :depends-on (#:alexandria
9 11
                #:blackbird
10 12
                #:chanl
... ...
@@ -40,3 +42,12 @@
40 42
                #:montezuma)
41 43
   :serial t
42 44
   :components ((:file "montezuma-store")))
45
+
46
+(defsystem :slacker/postmodern-store 
47
+  :description ""
48
+  :author "Ed L <edward@elangley.org>"
49
+  :license "MIT"
50
+  :depends-on (#:slacker
51
+               #:postmodern)
52
+  :serial t
53
+  :components ((:file "postmodern-store")))
43 54
new file mode 100644
... ...
@@ -0,0 +1,20 @@
1
+BEGIN;
2
+	ALTER TABLE slack_messages_raw
3
+	ADD COLUMN slack_channel text,
4
+	ADD COLUMN slack_user text;
5
+
6
+	UPDATE slack_messages_raw SET slack_channel = message->>'channel';
7
+	UPDATE slack_messages_raw SET slack_user = message->>'user';
8
+
9
+	CREATE OR REPLACE FUNCTION insert_messages_slack_ts() RETURNS TRIGGER AS $$
10
+	BEGIN
11
+		NEW.slack_ts := NEW.message->>'ts';
12
+		NEW.slack_channel := NEW.message->>'channel';
13
+		NEW.slack_user := NEW.message->>'user';
14
+
15
+		IF not NEW.message ? 'hidden' OR NEW.message->'hidden' <> to_jsonb(true) THEN
16
+			NEW.message_text := NEW.message #>> '{text}';
17
+		END IF;
18
+		RETURN NEW;
19
+	END;
20
+	$$ LANGUAGE PLPGSQL;
0 21
new file mode 100644
... ...
@@ -0,0 +1,28 @@
1
+BEGIN;
2
+
3
+	CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
4
+
5
+	DROP TABLE IF EXISTS slack_messages_raw CASCADE;
6
+	CREATE TABLE slack_messages_raw
7
+	(
8
+		id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
9
+		message JSONB,
10
+		message_text text,
11
+		slack_ts NUMERIC(32, 6)
12
+	);
13
+
14
+	CREATE OR REPLACE FUNCTION insert_messages_slack_ts() RETURNS TRIGGER AS $$
15
+	BEGIN
16
+		NEW.slack_ts := NEW.message->>'ts';
17
+		IF not NEW.message ? 'hidden' OR NEW.message->'hidden' <> to_jsonb(true) THEN
18
+			NEW.message_text := NEW.message #>> '{text}';
19
+		END IF;
20
+		RETURN NEW;
21
+	END;
22
+	$$ LANGUAGE PLPGSQL;
23
+
24
+	CREATE TRIGGER trig_insert_messages_slack_ts
25
+	BEFORE INSERT ON slack_messages_raw
26
+	FOR EACH ROW EXECUTE PROCEDURE insert_messages_slack_ts();
27
+
28
+COMMIT;
0 29
new file mode 100644
... ...
@@ -0,0 +1,31 @@
1
+(in-package :cl-user)
2
+(defpackage :foobar-baz (:use :cl))
3
+(in-package :foobar-baz)
4
+
5
+(defmacro eval-always (() &body body)
6
+  `(eval-when (:compile-toplevel :load-toplevel :execute)
7
+     ,@body))
8
+
9
+
10
+(eval-always ()
11
+  (ql:quickload :slacker)
12
+  (ql:quickload :slacker/montezuma-store)
13
+  (ql:quickload :slacker/postmodern-store)
14
+  (ql:quickload :data-lens)
15
+  (ql:quickload :osicat)
16
+  (ql:quickload :trivial-shell)
17
+  (ql:quickload :clack)
18
+  (ql:quickload :ningle)
19
+  (ql:quickload :araneus))
20
+
21
+(eval-always ()
22
+  (load (compile-file "/home/edwlan/words_coprocess-work/words-coprocess.lisp")))
23
+
24
+(eval-always ()
25
+  (load (compile-file (asdf:system-relative-pathname :slacker "hhgbot-2.lisp"))))
26
+
27
+#+(or)(eval-always ()
28
+  (load (compile-file "/home/edwlan/words_coprocess-work/words-coprocess.lisp")))
29
+  
30
+
31
+