(ns rooms (:use clojure.contrib.str-utils utils)) (def *run-flusher* true) (def *flusher-sleep* (seconds 4)) (def *user-timeout* (seconds 15)) (def rooms (ref {})) (def flusher (agent nil)) (defn flush-inactive-users! [x] (doseq [[rid room] @rooms] (dosync (let [users (room :users) now (System/currentTimeMillis) cutoff (- now *user-timeout*) alive? (fn [[n u]] (> (u :last-seen) cutoff))] (ref-set users (into {} (filter alive? (ensure users))))))) (Thread/sleep *flusher-sleep*) (when *run-flusher* (send *agent* #'flush-inactive-users!)) x) (defn start-user-flusher! [] (send flusher flush-inactive-users!)) (def *default-room* "dumpfm") (defn default-room? [key] (= (lower-case key) *default-room*)) (defn lookup-room [key] (@rooms (lower-case key))) (defn fetch-room [key] (first (do-select ["SELECT * FROM rooms WHERE key = LOWER(?)" key]))) (defn fetch-rooms [] (do-select ["SELECT * FROM ROOMS"])) (defn count-messages-by-room [room-id image-only] (let [query (str "SELECT COUNT(*) FROM messages m, users u WHERE room_id = ? AND m.user_id = u.user_id" (if image-only " AND m.is_image = true " ""))] (do-count [query room-id]))) (defn fetch-messages-by-room ([room-id image-only] (fetch-messages-by-room room-id image-only 0)) ([room-id image-only offset] (let [query (str "SELECT m.content, m.message_id, m.created_on, u.nick, u.avatar FROM users u, messages m WHERE room_id = ? AND m.user_id = u.user_id " (if image-only "AND m.is_image = true " "") "ORDER BY created_on DESC LIMIT ? OFFSET ?")] (do-select [query room-id *dumps-per-page* offset])))) (defn build-room-map-from-db [room-db] {:admin_only (room-db :admin_only) :room_id (room-db :room_id) :key (room-db :key) :name (room-db :name) :description (room-db :description) :users (ref {}) :messages (ref (fetch-messages-by-room (room-db :room_id) false)) :topic (ref nil) }) (defn load-rooms! [] (dosync (doseq [room-db (fetch-rooms)] (alter rooms assoc (lower-case (room-db :key)) (build-room-map-from-db room-db))))) ;; Room helpers (defn login-user [user room] (alter (room :users) assoc (user :nick) user)) (defn add-message [msg room] (alter (room :messages) (swap cons) msg)) (defn create-and-add-room! [key] (do-select ["INSERT INTO rooms (key, name, description) VALUES (?, ?, ?) RETURNING room_id" key key key]) (if-let [room-db (fetch-room key)] (dosync (alter rooms assoc (lower-case key) (build-room-map-from-db room-db)) room-db))) (defn get-or-create-room! [key] (:room_id (or (first (do-select ["SELECT room_id FROM rooms WHERE lower(key) = ?" (lower-case key)])) (create-and-add-room!) (throw (Exception. (str "Unable to create room " key)))))) (defn- get-or-create-room-bot-id! [nick] ((comp :user_id first) (or (do-select ["SELECT user_id FROM users WHERE lower(nick) = ?" (lower-case nick)]) (do-select ["INSERT INTO users (nick, hash, email) VALUES (?, ?, ?) RETURNING user_id" nick "GARBAGE" "info@dump.fm"])))) (defn get-or-create-room-bot! [key] (let [nick (str key "bo†")] [nick (get-or-create-room-bot-id! nick)]))