|
| 1 | +(uiop:define-package :starintel-gserver-client |
| 2 | + (:nicknames :star.api.client) |
| 3 | + (:use :cl) |
| 4 | + (:documentation "doc") |
| 5 | + (:export |
| 6 | + #:star-client |
| 7 | + #:make-url |
| 8 | + #:api-request |
| 9 | + #:get-targets |
| 10 | + #:new-target |
| 11 | + #:read-targets-csv |
| 12 | + #:import-targets-from-csv |
| 13 | + #:submit-document |
| 14 | + #:get-document |
| 15 | + #:fts |
| 16 | + #:messages-by-user |
| 17 | + #:messages-by-channel |
| 18 | + #:messages-by-platform |
| 19 | + #:messages-by-group |
| 20 | + #:social-posts-by-user |
| 21 | + #:dataset-size |
| 22 | + #:do-view |
| 23 | + #:groups)) |
| 24 | + |
| 25 | +(in-package :star.api.client) |
| 26 | +(defclass star-client () |
| 27 | + ((base-url :initform "http://127.0.0.1:5000" :initarg :base-url :accessor base-url) |
| 28 | + (headers :initform '(("Accept" . "application/json") |
| 29 | + ("Content-Type" . "application/json")) :initarg :headers :accessor star-client-headers)) |
| 30 | + (:documentation "The Starintel gserver api client")) |
| 31 | + |
| 32 | +(defun make-url (client api-url &key (query nil)) |
| 33 | + (if query |
| 34 | + (quri:merge-uris (quri:make-uri :path api-url :query (quri:url-encode-params query)) (base-url client)) |
| 35 | + (quri:merge-uris (quri:make-uri :path api-url) (base-url client)))) |
| 36 | + |
| 37 | + |
| 38 | +(defmacro api-request (client path &key |
| 39 | + (stream nil) |
| 40 | + (query nil) |
| 41 | + (content nil) |
| 42 | + (method :get) |
| 43 | + (force-binary nil) |
| 44 | + (keep-alive t)) |
| 45 | + |
| 46 | + |
| 47 | + `(let ((resp |
| 48 | + (dexador:request (make-url ,client ,path :query ,query) |
| 49 | + :method ,method :headers (star-client-headers ,client) :content ,content :want-stream ,stream :keep-alive ,keep-alive :force-binary ,force-binary))) |
| 50 | + |
| 51 | + |
| 52 | + |
| 53 | + resp)) |
| 54 | + |
| 55 | +(defmethod get-targets ((client star-client) actor-name) |
| 56 | + "Get all targets for ACTOR-NAME." |
| 57 | + (api-request client (format nil "/targets/~a" actor-name))) |
| 58 | + |
| 59 | +(defmethod new-target ((client star-client) target-doc actor &optional (transient nil)) |
| 60 | + "Insert new target." |
| 61 | + (if transient |
| 62 | + (api-request client (format nil "/new/target/~a" actor) :method :post |
| 63 | + :content (jsown:to-json (jsown:extend-js (jsown:parse target-doc) |
| 64 | + ("transient" t)))) |
| 65 | + (api-request client (format nil "/new/target/~a" actor) :method :post |
| 66 | + :content target-doc))) |
| 67 | + |
| 68 | +(defun read-targets-csv (targets-file) |
| 69 | + (let ((targets (data-table:select-columns (cl-csv:get-data-table-from-csv targets-file) (list "dataset" "target" "actor" "recurring" "delay" "options")))) |
| 70 | + (loop for row in (data-table:rows targets) |
| 71 | + collect (cons (nth 2 row) (with-output-to-string (str) |
| 72 | + (cl-json:encode-json |
| 73 | + (spec:new-target (nth 0 row) |
| 74 | + (nth 1 row) |
| 75 | + (nth 2 row) |
| 76 | + :options (nth 5 row) |
| 77 | + :recurring (cond ((string= (string-downcase (nth 3 row)) "true") t) |
| 78 | + ((string= (string-downcase (nth 3 row)) "t") t) |
| 79 | + ((string= (nth 3 row) "1") t) |
| 80 | + ((string= (nth 3 row) "0") nil) |
| 81 | + ((string= (string-downcase (nth 3 row)) "nil") nil) |
| 82 | + ((string= (string-downcase (nth 3 row)) "false") nil)) |
| 83 | + :delay (nth 4 row)) str)))))) |
| 84 | + |
| 85 | +(defmethod import-targets-from-csv ((client star-client) file) |
| 86 | + (loop for target in (read-targets-csv file) |
| 87 | + do (new-target client (cdr target) (car target)))) |
| 88 | + |
| 89 | +(defmethod submit-document ((client star-client) document document-type) |
| 90 | + "Create a new document" |
| 91 | + (api-request client (format nil "/new/document/~a" document-type) :method :post :content document)) |
| 92 | + |
| 93 | +(defmethod get-document ((client star-client) document-id) |
| 94 | + "Get the document by id." |
| 95 | + (api-request client (format nil "/document/~a" document-id))) |
| 96 | + |
| 97 | +(defmethod fts ((client star-client) &key q (limit 25) (bookmark nil) (sort nil)) |
| 98 | + "Search documents using the full-text search (FTS) endpoint." |
| 99 | + (let ((query (list (cons "q" q) |
| 100 | + (cons "limit" (prin1-to-string limit)) |
| 101 | + (cons "include_docs" "true")))) |
| 102 | + (when bookmark |
| 103 | + (push (cons "bookmark" bookmark) query)) |
| 104 | + (when sort |
| 105 | + (push (cons "sort" sort) query)) |
| 106 | + (api-request client "/search" :query query))) |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +(defmethod messages-by-user ((client star-client) &key user (limit 50) start-key end-key (descending nil) (skip 0)) |
| 111 | + "Retrieve messages by user using the 'messages_by_user' view." |
| 112 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 113 | + (cons "descending" (if descending "true" "false")) |
| 114 | + (cons "skip" (prin1-to-string skip))))) |
| 115 | + (when user |
| 116 | + (push (cons "user" user) query)) |
| 117 | + (when start-key |
| 118 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 119 | + (when end-key |
| 120 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 121 | + (api-request client "/documents/messages/by-user" :query query))) |
| 122 | + |
| 123 | +(defmethod messages-by-channel ((client star-client) group channel &key (limit 50) start-key end-key (descending nil) (skip 0) (reduce nil)) |
| 124 | + "Retrieve messages by group and channel using the 'by_channel' view." |
| 125 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 126 | + (cons "descending" (if descending "true" "false")) |
| 127 | + (cons "skip" (prin1-to-string skip)) |
| 128 | + (cons "reduce" (if reduce "true" "false")) |
| 129 | + (cons "channel" channel) |
| 130 | + (cons "group" (if reduce "true" "false"))))) |
| 131 | + (when start-key |
| 132 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 133 | + (when end-key |
| 134 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 135 | + (api-request client "/documents/messages/by-channel" :query query))) |
| 136 | + |
| 137 | +(defmethod messages-by-platform ((client star-client) &key platform (limit 50) start-key end-key (descending nil) (skip 0)) |
| 138 | + "Retrieve messages by platform using the 'messages_by_platform' view." |
| 139 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 140 | + (cons "descending" (if descending "true" "false")) |
| 141 | + (cons "skip" (prin1-to-string skip))))) |
| 142 | + (when platform |
| 143 | + (push (cons "platform" platform) query)) |
| 144 | + (when start-key |
| 145 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 146 | + (when end-key |
| 147 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 148 | + (api-request client "/documents/messages/by-platform" :query query))) |
| 149 | + |
| 150 | +(defmethod messages-by-group ((client star-client) &key (limit 50) start-key end-key (include-docs nil) (reduce nil) (descending nil) (skip 0)) |
| 151 | + "Retrieve messages by group using the 'messages_by_group' view." |
| 152 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 153 | + (cons "descending" (if descending "true" "false")) |
| 154 | + (cons "include-docs" (if include-docs "true" "false")) |
| 155 | + (cons "reduce" (if reduce "true" "false")) |
| 156 | + (cons "skip" (prin1-to-string skip))))) |
| 157 | + |
| 158 | + |
| 159 | + (when start-key |
| 160 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 161 | + (when end-key |
| 162 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 163 | + (api-request client "/documents/messages/by-group" :query query))) |
| 164 | + |
| 165 | +(defmethod social-posts-by-user ((client star-client) &key user (limit 50) start-key end-key (descending nil) (skip 0)) |
| 166 | + "Retrieve social media posts by user using the 'social_posts_by_user' view." |
| 167 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 168 | + (cons "descending" (if descending "true" "false")) |
| 169 | + (cons "skip" (prin1-to-string skip))))) |
| 170 | + (when user |
| 171 | + (push (cons "user" user) query)) |
| 172 | + (when start-key |
| 173 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 174 | + (when end-key |
| 175 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 176 | + (api-request client "/documents/socialmpost/by-user" :query query))) |
| 177 | + |
| 178 | +(defmethod dataset-size ((client star-client) dataset) |
| 179 | + "Retrieve the size of a dataset using the 'dataset_size' view." |
| 180 | + (let ((query (list (cons "dataset" dataset)))) |
| 181 | + (api-request client "/dataset-size" :query query))) |
| 182 | + |
| 183 | + |
| 184 | +(defmethod groups ((client star-client) &key (limit 50) |
| 185 | + (start-key nil) (end-key nil) |
| 186 | + (update :lazy) |
| 187 | + (descending nil) (skip 0)) |
| 188 | + "Retrieve messages by group using the 'messages_by_group' view. |
| 189 | + The result of this api is [{\"key\": \"group-name\", \"value\": [\"chanel-list\"]}]" |
| 190 | + (let ((query (list (cons "limit" (prin1-to-string limit)) |
| 191 | + (cons "descending" (if descending "true" "false")) |
| 192 | + (cons "skip" (prin1-to-string skip))))) |
| 193 | + |
| 194 | + |
| 195 | + (case update |
| 196 | + (:lazy (push (cons "update" "lazy") query)) |
| 197 | + (:false (push (cons "update" "false") query)) |
| 198 | + (t (push (cons "update" "true") query))) |
| 199 | + |
| 200 | + (when start-key |
| 201 | + (push (cons "start_key" (cl-json:encode-json-to-string start-key)) query)) |
| 202 | + (when end-key |
| 203 | + (push (cons "end_key" (cl-json:encode-json-to-string end-key)) query)) |
| 204 | + |
| 205 | + |
| 206 | + (api-request client "/documents/messages/groups" :query query))) |
| 207 | + |
| 208 | + |
| 209 | +;; (defun do-view (client view-fn &key (batch-size 500) (reduce nil) (include-docs nil)) |
| 210 | +;; "Iterate over all keys in a view and apply the provided function to each batch of results." |
| 211 | +;; (let ((start-key nil) |
| 212 | +;; (end-key nil) |
| 213 | +;; (results '()) |
| 214 | +;; (batch (funcall view-fn client |
| 215 | +;; :limit batch-size |
| 216 | +;; :include-docs (when (and (not reduce) include-docs) t) |
| 217 | +;; :reduce reduce))) |
| 218 | +;; (let* () |
| 219 | +;; |
| 220 | +;; |
| 221 | +;; batch))) |
0 commit comments