M src/client.lisp => src/client.lisp +16 -6
@@ 10,11 10,12 @@
")
;(import-from #:pixie/clients/fs)
(:export
+ make-client-system
make-client
account-names
account
- make-account
whoami
+ room-names
;conversations
;connect
;name
@@ 30,7 31,7 @@
(in-package #:skin.djha.pixie/client)
-(defgeneric make-account (kind specifics)
+(defgeneric make-client (kind specifics)
(
:documentation
"
@@ 48,13 49,22 @@
)
)
-(defclass root ()
+(defgeneric room-names (account)
+ (
+ :documentation
+ "
+ Return a list of room names.
+ "
+ )
+ )
+
+(defclass client-system ()
((accounts :initarg :accounts
:initform (error "Must specify accounts.")
:accessor accounts
:type hash-table)))
-(defun make-client (config)
+(defun make-client-system (config)
(let ((account-objects (make-hash-table :test #'equal)))
(loop for slug being the hash-keys of (gethash :accounts config)
using (hash-value payload)
@@ 65,10 75,10 @@
using (hash-value specifics)
do
(setf (gethash slug account-objects)
- (skin.djha.pixie/client:make-account
+ (skin.djha.pixie/client:make-client
kind
specifics)))))
- (make-instance 'root :accounts account-objects)))
+ (make-instance 'client-system :accounts account-objects)))
(defun account-names (client)
(loop for acc being the hash-keys of (accounts client)
M src/clients/groupme.lisp => src/clients/groupme.lisp +86 -70
@@ 52,7 52,7 @@
)
)
-(defmethod skin.djha.pixie/client:make-account ((kind (eql :groupme)) specifics)
+(defmethod skin.djha.pixie/client:make-client ((kind (eql :groupme)) specifics)
(declare (type hash-table specifics))
(make-instance 'groupme-client
:api-token (gethash :api-token specifics)
@@ 63,11 63,10 @@
(defun simple-get
(
client
+ path
&key
query
- path
)
- (format t "API Token: ~A" (api-token client))
(multiple-value-bind
(response code headers redir)
(let ((q (acons "token" (api-token client) query)))
@@ 86,15 85,84 @@
(error "bad")
(let ((unwrapped (with-input-from-string (strm response)
(nrdl:parse-from strm))))
- (format t "Unwrapped ~A~%" (nrdl:nested-to-alist unwrapped))
- (format t "Response ~A~%" (nrdl:nested-to-alist (gethash "response" unwrapped)))
(gethash "response" unwrapped)))))
+(defun adjoin-entity
+ (nick id ids)
+ "
+ This function collapses all known nicknames for a given set of ids,
+ choosing the longest nickname for each entity id.
+ This helps to build a mapping between ids and nicknames.
+ "
+ (let ((prior (assoc id ids)))
+ (when (or (not prior)
+ (< (length (cdr prior))
+ (length nick)))
+ (acons id nick ids))))
+
+(defun insert-by-name (nick id mapping)
+ "
+ Insert a `'<nick>' -> id` mapping into a
+ larger mapping from nicknames to ids.
+
+ If the nick already exists, attempt to insert '<nick> 1'.
+ If that exists, attempt to insert '<nick> 2', and so forth.
+ If the id is already present in the mapping under one of the
+ attempted nicknames, then do nothing.
+ "
+ (loop
+ for i = 1 then (+ i 1)
+ for tried = nick then (format nil "~A ~A" nick i)
+ for prior-id = (gethash tried mapping)
+ while prior-id
+ do
+ (when (equal prior-id id)
+ (return))
+ finally
+ (setf
+ (gethash tried mapping)
+ id))
+ mapping)
+
+(defun names-ids (response)
+ "
+ Return a hash table mapping nicknames to ids given the API response.
+ "
+ (let ((collapsed-nicks
+ (loop for record in response
+ for nick = (gethash "name" record)
+ for id = (gethash "id" record)
+ with accumulate = nil
+ do
+ (adjoin-entity nick id accumulate)
+ finally
+ (return
+ (alexandira:alist-hash-table
+ (sort accumulate (lambda (a b)
+ ; Sort based on lexicographic sort
+ ; of nicknames.
+ (string< (cdr a) (cdr b)))))))))
+ (loop for id being the hash-keys of collapsed-nicks
+ using (hash-value nick)
+ with accumulate = (make-hash-table :test #'equal)
+ do
+ (insert-by-name
+ nick
+ id
+ accumulate)
+ finally
+ (return accumulate))))
+
(defmethod skin.djha.pixie/client:whoami
((client groupme-client))
(simple-get client
- :path '("users" "me")))
+ '("users" "me")))
+(defmethod skin.djha.pixie/client:room-names
+ ((client groupme-client))
+ (names-ids (groupme-get '("groups")
+ :query '(("omit" . "memberships")))))
+
;(defun get-group-messages
; (
; client
@@ 137,75 205,23 @@
; :host (host client)
; :query q)))
;
-(defun get-paginated
- (
- client
- &key
- query
- path
- (page 1)
- )
- (declare (type groupme-client client)
- (type list query)
- (type integer page))
- (multiple-value-bind
- (response code headers redir)
- (dexador:get
- (let* ((q (acons "token" (api-token client) query))
- (q (acons "page" page q)))
- (quri:make-uri
- :scheme (scheme client)
- :host (host client)
- :path (format nil "/~A~{/~A~}"
- (base-path client)
- path)
- :query q)))
- (declare (ignore headers)
- (ignore redir))
- (if (> code 399)
- (error "bad")
- (gethash
- "response"
- (with-input-from-string (strm response)
- (nrdl:parse-from strm))))))
(defun groupme-get
- (&rest groupme-get-paginated-args)
- (apply
- #'concatenate
- (cons 'vector
- (loop for page = 1 then (+ page 1)
- for response = (apply
- #'get-paginated
- (concatenate
- 'list
- groupme-get-paginated-args
- `(:page ,page)))
+ (client path &key query)
+ (loop with accumulate = (make-array 10 :fill-pointer t)
+ for page = 1 then (+ page 1)
+ for response = (simple-get client path (acons "page" page query))
while (> (length response) 0)
- collect response))))
+ do
+ (loop for msg across response
+ do
+ (vector-push-extend msg accumulate))
+ finally
+ (return (coerce accumulate 'list))))
+
+
-(defun adjoin-entity
- (nick id ids)
- (let ((prior (gethash id ids)))
- (when (or (not prior)
- (< (length prior)
- (length nick)))
- (setf (gethash id ids) nick))))
-(defun insert-by-name (k v result)
- (loop
- for i = 1 then (+ i 1)
- for tried = k then (format nil "~A ~A" k i)
- for prior = (gethash tried result)
- while prior
- do
- (when (equal prior v)
- (return))
- finally
- (setf
- (gethash tried result)
- v))
- result)
;(defun ids-names
; (ids)
M src/main.lisp => src/main.lisp +30 -20
@@ 35,32 35,38 @@
(:options . ,options))
:test #'equal)))))
-;; TODO: Account-list
(defun account-list (options)
(declare (type hash-table options))
(alexandria:alist-hash-table `((:status . :successful)
(:accounts .
,(skin.djha.pixie/client:account-names
- (skin.djha.pixie/client:make-client
+ (skin.djha.pixie/client:make-client-system
options))))
:test #'equal))
+(defun client-operation (options opspec op)
+ (declare (type hash-table options)
+ (type function op))
+ (if (null (gethash :account options))
+ (alexandria:alist-hash-table
+ `((:status . :cl-usage-error)
+ (:options . ,options)
+ (:error . "No account specified"))
+ :test #'equal)
+ (let ((client (skin.djha.pixie/client:make-client-system options)))
+ (alexandria:alist-hash-table
+ `((:status . :successful)
+ (opspec .
+ ,(funcall
+ op
+ (skin.djha.pixie/client:account
+ client
+ (gethash :account options)))))
+ :test #'equal))))
+
(defun whoami (options)
(declare (type hash-table options))
- (if (null (gethash :account options))
- (alexandria:alist-hash-table `((:status . :cl-usage-error)
- (:options . ,options)
- (:error . "No account specified"))
- :test #'equal)
- (let ((client (skin.djha.pixie/client:make-client options)))
- (format t "~A" (skin.djha.pixie/client:account-names client))
- (alexandria:alist-hash-table `((:status . :successful)
- (:whoami .
- ,(skin.djha.pixie/client:whoami
- (skin.djha.pixie/client:account
- client
- (gethash :account options)))))
- :test #'equal))))
+ (client-operation options :whoami #'skin.djha.pixie/client:whoami))
(defun history (options)
(declare (type hash-table options))
@@ 74,15 80,18 @@
(declare (type hash-table options))
(stub options "watch"))
-(defun list-conversations (options)
+(defun room-list (options)
(declare (type hash-table options))
- (stub options "list-conversations"))
+ (client-operation options :rooms #'skin.djha.pixie/client:room-names))
(defun help (options)
(declare (type hash-table options))
(stub options "help"))
(defun main (argv &key (strm t))
+ "
+ The functional entrypoint of the pixie command.
+ "
(declare (type list argv))
(cl-i:execute-program
"pixie"
@@ 94,7 103,7 @@
(("history") . ,#'history)
(("post") . ,#'post)
(("watch") . ,#'watch)
- (("conversations") . ,#'list-conversations)
+ (("room" "list") . ,#'room-list)
(("help") . ,#'help)
)
:cli-arguments argv
@@ 109,7 118,8 @@
;(skin.djha.pixie:main '("whoami" "--set-account" "djhaskin987"))
;(skin.djha.pixie:main '("account" "list"))
-
+;; TODO: Aggregate response, deal with symbols.
+;(skin.djha.pixie:main '("room" "list" "--set-account" "djhaskin987"))
(defun entrypoint (argv)
(uiop:quit
(main argv :strm *standard-output*)))