diff options
| -rw-r--r-- | src/admin.clj | 38 | ||||
| -rw-r--r-- | src/scheduled_agent.clj | 31 | ||||
| -rw-r--r-- | src/site.clj | 30 | ||||
| -rwxr-xr-x | src/utils.clj | 7 | ||||
| -rw-r--r-- | static/js/admin.js | 37 | ||||
| -rw-r--r-- | static/js/pichat.js | 2 | ||||
| -rw-r--r-- | template/chat.st | 16 |
7 files changed, 116 insertions, 45 deletions
diff --git a/src/admin.clj b/src/admin.clj index a7eced1..a3fc076 100644 --- a/src/admin.clj +++ b/src/admin.clj @@ -3,6 +3,7 @@ (:require [clojure.contrib.str-utils2 :as s]) (:use compojure email + scheduled-agent utils)) ;; Debug Page @@ -46,6 +47,24 @@ ;; Muting +(def *mute-refresh-period-sec* 60) + +(def fetch-mutes-query " +SELECT *, set_on + duration AS expiry +FROM mutes +WHERE (set_on + duration) < now() +AND NOT is_canceled +") + +(defn update-mutes [] + (let [res (do-select [fetch-mutes-query])] + (zipmap (map :user_id res) res))) + +(def *active-mutes* + (scheduled-agent (no-args-adaptor update-mutes) + *mute-refresh-period-sec* + nil)) + (defn mute-status [session] (if-vip (println session))) @@ -59,11 +78,16 @@ (defn mute! [session params] (if-vip - (let [nick (params :user) - user_id (:user_id (fetch-nick nick)) - interval (parse-pos-interval (params :time) (params :unit)) + (let [nick (params :nick) + user-id (:user_id (fetch-nick nick)) + duration (parse-pos-interval (params :time) (s/butlast (params :unit) 1)) reason (params :reason) - admin-user-id (session :user_id)] - (cond (not user_id) [400 "INVALID_NICK"] - (not interval) [400 "INVALID_INTERVAL"] - :else (do "OK"))))) + admin-id (session :user_id)] + (cond (not user-id) [400 "INVALID_NICK"] + (not duration) [400 "INVALID_DURATION"] + ;; TODO: Ugly interval hack, w/ no escaping. Totally unsafe. + :else (let [q (format "INSERT INTO mutes (user_id, admin_id, duration, reason) + VALUES (%s, %s, '%s', '%s')" + user-id admin-id duration reason)] + (and (do-cmds q) "OK")))))) + diff --git a/src/scheduled_agent.clj b/src/scheduled_agent.clj new file mode 100644 index 0000000..702b314 --- /dev/null +++ b/src/scheduled_agent.clj @@ -0,0 +1,31 @@ +(ns scheduled-agent + (:import java.util.concurrent.Executors + java.util.concurrent.TimeUnit) + (:use clojure.stacktrace)) + +(defn- runnable-proxy [f] + (proxy [Runnable] [] (run [] (f)))) + +(defn scheduled-agent + [func period init] + (let [pool (Executors/newScheduledThreadPool 1) + r (ref init) + pfunc (runnable-proxy (fn [] + (try + (dosync + (ref-set r (func (ensure r)))) + (catch Exception e + (print-stack-trace e 5))))) + future (.scheduleWithFixedDelay pool pfunc 0 period TimeUnit/SECONDS)] + {:pool pool + :data r + :future future + :func pfunc + :period period + :init init})) + +(defn cancel [{f :future}] + (.cancel f false)) + +(defn poll [{d :data}] + @d) diff --git a/src/site.clj b/src/site.clj index 57f9bb1..234f5da 100644 --- a/src/site.clj +++ b/src/site.clj @@ -21,7 +21,8 @@ cookie-login session-sweeper feed - tags)) + tags + scheduled-agent)) (def *run-flusher* true) (def *flusher-sleep* (seconds 4)) @@ -351,11 +352,11 @@ invalid-nick-reason (is-invalid-nick? nick)] (cond invalid-nick-reason (resp-error invalid-nick-reason) (check-nick nick) (resp-error "NICK_TAKEN") - :else (with-connection *db* - (insert-values :users - [:nick :hash :email] - [nick hash email]) - (let [db-user (fetch-nick nick)] + :else (do + (do-insert :users + [:nick :hash :email] + [nick hash email]) + (let [db-user (fetch-nick nick)] (send-registration-email nick email) [(session-assoc-from-db db-user) (resp-success "OK")]))))) @@ -632,14 +633,19 @@ :message_id)))) (defn msg [session params] - (let [user-id (session :user_id) - nick (session :nick) - room-key (params :room) - room (lookup-room room-key) - content (.trim (params :content)) - now (new Date)] + (let [user-id (session :user_id) + mute ((poll *active-mutes*) user-id) + nick (session :nick) + room-key (params :room) + room (lookup-room room-key) + content (.trim (params :content)) + now (new Date)] (cond (not room) (resp-error "BAD_ROOM") (not nick) (resp-error "NOT_LOGGED_IN") + mute (resp-error + (format (str "I'm sorry, you've been muted for %s. " + "You'll be able to post again on %s EST.") + (mute :reason) (mute :expiry))) :else (let [msg-id (msg-db user-id (room :room_id) content) msg (struct message-struct nick content now msg-id)] diff --git a/src/utils.clj b/src/utils.clj index ea696e5..da4d4be 100755 --- a/src/utils.clj +++ b/src/utils.clj @@ -20,6 +20,9 @@ ;; Misc +(defn no-args-adaptor [f] + (fn [& more] (f))) + (defn ms-in-future [ms] (+ ms (System/currentTimeMillis))) @@ -59,6 +62,10 @@ ;; Database +(defn do-cmds [query] + (with-connection *db* + (do-commands query))) + (defn do-select [query] (with-connection *db* (with-query-results rs query diff --git a/static/js/admin.js b/static/js/admin.js index a5a5f71..e6bef70 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -26,7 +26,7 @@ Admin.mute = function(nick) { .append(reason) .appendTo($(Admin._dialogHtml)); var title = 'Mute ' + nick; - var cancel = function() { html.dialog('close'); } + var close = function() { html.dialog('close'); } var submit = function() { html.find('[name]').removeClass('ui-state-error'); @@ -34,26 +34,29 @@ Admin.mute = function(nick) { var u = unit.val(); var r = reason.val(); - if (!t) { - time.addClass('ui-state-error'); - } + if (!t) { time.addClass('ui-state-error'); } + if (!u) { reason.addClass('ui-state-error'); } + if (!r) { reason.addClass('ui-state-error'); } + if (!t || !u || !r) { return; } - if (!u) { - reason.addClass('ui-state-error'); - } - - if (!r) { - reason.addClass('ui-state-error'); - } - - if (!t || !u || !r) { - return; - } + $.ajax({ + type: 'POST', + timeout: 5000, + url: '/mute', + cache: false, + data: { 'time': t, 'unit': u, + 'reason': r, 'nick': nick }, + success: close, + error: function(s) { + alert("Error muting user: " + s.responseText); + } + }); }; html.dialog({ - modal: true, + modal: false, title: title, width: 400, - buttons: { 'OK': submit , 'Cancel': cancel } + buttons: { 'OK': submit , 'Cancel': close } }); + html.dialog('open'); };
\ No newline at end of file diff --git a/static/js/pichat.js b/static/js/pichat.js index d6af36f..d52212d 100644 --- a/static/js/pichat.js +++ b/static/js/pichat.js @@ -221,7 +221,7 @@ function handleMsgError(resp) { if (respText == 'MUST_LOGIN') { alert("Can't send message! Please login."); } else if (respText) { - alert("Can't send message! (" + respText + ")"); + alert("Can't send message! " + respText); } else { alert("Can't send message!"); } diff --git a/template/chat.st b/template/chat.st index 8a02ccd..239f5b9 100644 --- a/template/chat.st +++ b/template/chat.st @@ -44,17 +44,17 @@ $banner()$ <div id="loghead"></div> </div> <div id="userList"> -$users: { u | + $users: { u | <div class="username"><a href="/u/$u.nick$" target="_blank"> - $if(u.avatar)$ - <img src="$u.avatar$" width="50" height="50"> - $else$ - <img src="/static/img/noinfo.png"> - $endif$ + $if(u.avatar)$ + <img src="$u.avatar$" width="50" height="50"> + $else$ + <img src="/static/img/noinfo.png"> + $endif$ $u.nick$</a><br> </div> -}$ - </div> + }$ + </div> <div id="messagePane"> <div id="messageList"> $messages: { m | |
