1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
(ns rooms
(:import java.util.Date)
(:use clojure.contrib.str-utils
clojure.contrib.def
utils
user))
(defstruct message-struct :nick :content :created_on :msg_id)
(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]
(and key (@rooms (lower-case key))))
(defn fetch-room [key]
(first (do-select ["SELECT * FROM rooms WHERE key = LOWER(?) AND active" key])))
(defn fetch-rooms []
(do-select ["SELECT * FROM ROOMS WHERE active"]))
(defn lookup-room-key [room-id]
(or (some #(and (= (:room_id %) room-id) (:room_key %))
(vals @rooms))
(:key (first (do-select ["SELECT key FROM rooms where room_id = ?" room-id])))))
(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)
:max_file_size (room-db :max_file_size)
:max_image_width (room-db :max_image_width)
:max_image_height (room-db :max_image_height)
:history_size (room-db :history_size)
: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))
; Note: To ensure that the msg's timestamp is consistent
; with other msg creations, build-msg must be used
; within a dosync.
(defn build-msg [nick content msg-id]
(struct message-struct nick content (new Date) msg-id))
(defn add-message [msg room]
(insert-and-truncate! (room :messages) msg message-count-limit))
(defn insert-message-into-db! [user-id room-id content is-image]
(:message_id
(first
(do-select ["INSERT INTO messages (user_id, room_id, content, is_image)
VALUES (?, ?, ?, ?) RETURNING message_id"
user-id room-id content is-image]))))
(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)))
; TODO: cache
(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! key)
(throw (Exception. (str "Unable to create room " key))))))
(defn fetch-or-create-bot-id! [nick]
(:user_id
(or (fetch-nick nick)
(first
(do-select ["INSERT INTO users (nick, hash, email)
VALUES (?, ?, ?) RETURNING user_id"
nick "GARBAGE" "info@dump.fm"])))))
(def room-bot-id-cache (ref {}))
(defn get-or-create-room-bot! [room-key]
(let [nick (str "~" room-key)]
(or (get @room-bot-id-cache nick)
(let [id (fetch-or-create-bot-id! nick)
r [nick id]]
(dosync
(commute room-bot-id-cache assoc nick r))
r))))
|