             :code 431
             :msg ":No nickname given"})))
 
-(defcommand "USER" [channel parts]
+(defcommand* "USER" [channel parts]
   (dosync
    (update-user-for-nick! (nick-for-channel channel) parts)
    (maybe-add-authentication-step! channel "USER")

(defn valid-nick? [nick]
  (not (re-find #"[^a-zA-Z0-9\-\[\]\'`^{}_]" nick)))

(defcommand "NICK" [channel nick]
  (if (not (.isEmpty nick))
    (if (valid-nick? nick)
      (if (not (user-for-nick nick))

    (dispatch-command cmd socket (or args ""))))

(defn fix-args
  [require-auth? fn-tail]
  (let [[f & r] fn-tail]
    `(~(vec (cons '_ f))
      ~(if require-auth?
         `(when (authenticated? ~(first f))
            ~@r)
         `(do ~@r)))))

(defmacro defcommand*
  "Define a command which can be called by unauthenticated users."
  [cmd & fn-tail]
  `(.addMethod dispatch-command ~cmd
               (fn ~@(fix-args false fn-tail))))

(defmacro defcommand
  "Define a command which requires its user to be authenticated."
  [cmd & fn-tail]
  `(.addMethod dispatch-command ~cmd
               (fn ~@(fix-args true fn-tail))))

(defmethod dispatch-command :default [cmd channel args]
  (when (authenticated? channel)
