(in-package :cl-user) (defpackage chatikbot.poller (:use :cl :chatikbot.db :chatikbot.utils :chatikbot.secrets) (:export :*poller-token* :*poller-module* :rest-parameters :poller-request :poller-validate :poller-get-token :poller-error :poller-no-secret :poller-cant-get-token :poller-cant-authenticate :poller-call :poller-poll-lists :poller-authenticate)) (in-package :chatikbot.poller) (defvar *tokens* (make-hash-table) "Module's tokens store") (defvar *state* (make-hash-table) "Module's state store") (defvar *poller-token* nil "Current user's API token") (defvar *poller-module* nil "Current module") (defun rest-parameters (rest &optional raw) (loop for (param value) on rest by #'cddr when value collect (cons (if raw (string param) (dekeyify param)) value))) (defun get-data (store chat-id &optional (module *poller-module*)) (let ((module-store (gethash module store))) (when module-store (gethash chat-id module-store)))) (defun set-data (store chat-id data &optional (module *poller-module*)) (let ((module-store (or (gethash module store) (setf (gethash module store) (make-hash-table))))) (setf (gethash chat-id module-store) data))) (defgeneric poller-request (module method &rest params) (:documentation "Performs api request to module")) (defgeneric poller-validate (module response) (:documentation "Performs api result validation")) (defgeneric poller-get-token (module secret) (:documentation "Performs token generation out of module")) (define-condition poller-error (error) ()) (define-condition poller-no-secret (poller-error) ()) (define-condition poller-cant-get-token (poller-error) ()) (define-condition poller-cant-authenticate (poller-error) ()) (defun poller-call (module method &rest params) (let* ((chat-id *chat-id*) (*poller-module* module) (*poller-token* (get-data *tokens* chat-id module)) (response (apply 'poller-request module method params))) (if (poller-validate module response) response (with-secret (secret (list module chat-id)) (unless secret (error 'poller-no-secret)) (let ((*poller-token* (poller-get-token module secret))) (unless *poller-token* (error 'poller-cant-get-token)) (set-data *tokens* chat-id *poller-token* module) (values (apply 'poller-request module method params))))))) (defun poller-authenticate (module secret) (let ((token (poller-get-token module secret))) (unless token (error 'poller-cant-authenticate)) (secret-set (list module *chat-id*) secret) token)) (defun poller-poll-lists (module get-state-fn process-diff-fn &key (test #'equalp) (predicate #'<) key (max-store 200)) (dolist (*chat-id* (lists-get module)) (handler-case (let* ((old (get-data *state* *chat-id* module)) (new (funcall get-state-fn)) (diff (sort (set-difference new old :test test) predicate :key key))) (when diff (when old (funcall process-diff-fn diff)) (let ((merged (merge 'list old diff predicate :key key))) (set-data *state* *chat-id* (subseq merged (max (- (length merged) max-store) 0)) module)))) (error (e) (log:error e)))))