(defvar *commands* (make-hash-table :test 'equalp)) (defun command (name) (or (gethash (string name) *commands*) (error "No command named ~s found." name))) (defun (setf command) (func name) (setf (gethash (string name) *commands*) func)) (defun remove-command (name) (remhash (string name) *commands*)) (defmacro define-command (name (event &rest args) &body body) (let ((argsg (gensym "ARGS"))) `(setf (command ',name) (lambda (,event &rest ,argsg) (destructuring-bind ,args ,argsg ,@body))))) (define-command ping (event) (reply event "pong")) (define-command echo (event &rest args) (reply event "~{~a~^ ~}" args)) (defun process-event (event) (let ((message (message event))) (when (and (< 1 (length message)) (char= #\/ (char message 0))) (handler-case (let ((args (cl-ppcre:split " +" message :start 1))) (apply (command (first args)) event (rest args))) (error (err) (reply event "Error: ~a" err)))))) ;;; Sample event implementation (defclass repl-event () ((message :initarg :message :reader message))) (defmethod reply ((event repl-event) fmt &rest args) (apply #'format T fmt args)) ;;; Tests (defun repl-message (message) (process-event (make-instance 'repl-event :message message))) ;; (repl-message "/ping") ;; (repl-message "/echo foo") ;; (repl-message "/fart") ;; (repl-message "/ping someone")