(in-package #:logbot) (defun get-and-purge-outbox-messages (db target) (postmodern:with-connection db (postmodern:query "with deleted as ( delete from outbox where target = $1 returning message, queued_at ) select message from deleted order by queued_at" target :rows))) (defun make-log-entry (db target message host source user) (postmodern:with-connection db (postmodern:execute "insert into log (target, message, host, source, \"user\") values ($1, $2, $3, $4, $5)" target message (if (string= "" host) :null host) source (if (null user) :null user)))) (defclass logbot (ircbot) ((pg-thread :accessor logbot-pg-thread :initform nil) (db :reader logbot-db :initarg :db))) (defmethod ircbot-connect :after ((bot logbot)) (let ((conn (ircbot-connection bot))) (add-hook conn 'irc-mode-message (lambda (message) (logbot-check-mode bot message))) (add-hook conn 'irc-privmsg-message (lambda (message) (destructuring-bind (target message-text) (arguments message) (make-log-entry (logbot-db bot) target message-text (host message) (source message) (user message))))))) (defmethod ircbot-send-message :after ((bot logbot) target message-text) (let* ((b-connection (ircbot-connection bot)) (b-user (user b-connection))) (make-log-entry (logbot-db bot) target message-text (hostname b-user) (nickname b-user) (username b-user)))) (defmethod logbot-check-mode ((bot logbot) message) (if (= 3 (length (arguments message))) (destructuring-bind (channel mode nick) (arguments message) (when (and (string= (host message) "services.") (member channel (ircbot-channels bot) :test #'string=) (or (string= mode "+o") (string= mode "+v")) (string= nick (ircbot-nick bot))) (when (null (logbot-pg-thread bot)) (logbot-start-pg-thread bot) (logbot-send-outbox bot channel)))))) (defmethod logbot-send-outbox ((bot logbot) target) (loop for (message) in (get-and-purge-outbox-messages (logbot-db bot) target) do (ircbot-send-message bot target message))) (defmethod logbot-start-pg-thread ((bot logbot)) (setf (logbot-pg-thread bot) (sb-thread:make-thread (lambda () (postmodern:with-connection (logbot-db bot) (postmodern:execute "listen outbox_new_message") (loop (multiple-value-bind (channel payload pid) (cl-postgres:wait-for-notification postmodern:*database*) (declare (ignore pid)) (if (string= channel "outbox_new_message") (logbot-send-outbox bot payload)))))) :name "logbot-pg"))) (defun make-logbot (server port nick password channels db) (make-instance 'logbot :server server :port port :nick nick :password password :channels channels :db db))