Source

messageme-clj / src / messageme / core.clj

Full commit
(ns messageme.core)

;; Agent for the list of messages in the system.
(def message-box (agent ()))

;; Agent for the vector of currently connected users in the system.
(def connected-users (agent []))

(defn print-agent-error
  "Print the exception from the asyncrhonous agent"
  [agent-name exception]
  (println "Exception:" exception "occured in agent:" @agent-name))

;; Print errors for debugging purposes.
(set-error-handler! message-box print-agent-error)
(set-error-handler! connected-users print-agent-error)

(defn do-connect-user
  "Add user to the vector of connected users. This will cause new message
  notifications to be printed for this user."
  [user]
  (send connected-users (fn [connected-users]
                              (conj connected-users user))))

(defn do-disconnect-user
  "Remove user from the vector of connected users, thus preventing notifications
  from displaying."
  [user]
  (send connected-users (fn [connected-users]
                          (vec (remove #(= % user) connected-users)))))

(defn do-insert-message
  "Add a new message to the list of messages in the system"
  [message]
  (send message-box (fn [box]
                          (conj box message))))

(defn do-notify-recipient
  "Print a new message notification if the recipient user is connected."
  [msg]
  (send connected-users
            (fn [users]
              (if (some #{(:recipient msg)} users)
                (println " ** Notification:" (:recipient msg)
                         "has a new message from" (:sender msg))) users)))

(defn send-message
  "Build a map representing the new message then send it to the message-box
  agent. Also triggers a new message notification."
  [sender recipient body]
  (let [msg {:sender sender, :recipient recipient, :body body :read false}]
    (do-insert-message msg)
    (do-notify-recipient msg)))

(defn filter-user-messages
  "Return a function which filters messages belonging to the given user."
  [user]
  (fn [message-box] (filter (fn [msg] (= (:recipient msg) user)) message-box)))

(defn read-msg
  "Displays a message and returns a map representing the read message"
  [msg]
  (println "  From:" (:sender msg) "| Message:" (:body msg))
  (assoc msg :read true))

(defn display-inbox
  "Search the message-box for messages belonging to user.
  If messages are found, read and display them.
  Returns a complete message-box with the user's messages marked read."
  [message-box user]
  (let [msgs (filter #(= (:recipient %) user) message-box)
        other-mail (remove #(= (:recipient %) user) message-box)]
    (println "Inbox for" user "contains" (count msgs) "messages:")
    (concat (map read-msg msgs) other-mail)))

(defn display-unread
  "Search the message-box for unread messages belonging to user.
  If messages are found, read and display them.
  Returns a complete message-box with the user's messages marked read."
  [message-box user]
  (let [msgs (filter #(= (:recipient %) user) message-box)
        unread-msgs (remove #(:read %) msgs)
        read-msgs (filter #(:read %) msgs)
        other-mail (remove #(= (% :recipient) user) message-box)]
    (println (count unread-msgs) "arrived since you last connected:")
    (concat (map read-msg unread-msgs) read-msgs other-mail)))

(defn do-display-inbox
  "Display a brief header, then display each message, marking it as read."
  [user]
  (send message-box display-inbox user))

(defn do-display-unread
  "Display a header with the number of unread messages followed by each unread
  message. This function marks all messages the user's messages read."
  [user]
  (send message-box display-unread user))