tree checksum vpatch file split hunks
all signers:
antecedents:
press order:
logbot-genesis |
patch:
(0 . 0)(1 . 26)
5 INSTALL
6
7 * Install `ircbot`.
8
9 * From the SBCL REPL:
10 (ql:quickload :cl-irc)
11 (ql:quickload :cl-postgres)
12 (ql:quickload :postmodern)
13
14 * Use V to press `logbot`
15
16 mkdir -p ~/src/logbot
17 cd ~/src/logbot
18
19 mkdir .wot
20 cd .wot && wget http://trinque.org/trinque.asc && cd ..
21
22 v.pl init http://trinque.org/src/logbot
23 v.pl press logbot-genesis logbot-genesis.vpatch
24
25 ln -s ~/src/logbot/logbot-genesis ~/quicklisp/local-projects/logbot
26
27 * Create a PostgreSQL database with UTF-8 encoding, then load logbot.sql
28 into that database:
29
30 psql -f logbot.sql mydb
-(0 . 0)(1 . 7)
35 README
36
37 `logbot` extends `ircbot` to provide an interface to a single IRC
38 channel from PostgreSQL, writing all messages to a `log` table and
39 reading messages to be sent from `outbox` in a specified database. By
40 listening to pg_notify channels, services connected to PostgreSQL can
41 react when new log lines are inserted.
-(0 . 0)(1 . 14)
46 USAGE
47
48 (asdf:load-system :logbot)
49 (defvar *bot*)
50 (setf *bot*
51 (logbot:make-logbot
52 "chat.freenode.net" 6667 "nick" "password" "#channel"
53 '("db-name" "db-user" "db-password" "db-host")))
54
55 ; connect in separate thread, returning thread
56 (logbot:ircbot-connect-thread *bot*)
57
58 ; or connect using the current thread
59 ; (logbot:ircbot-connect *bot*)
-(0 . 0)(1 . 13)
64 ;;;; logbot.asd
65
66 (asdf:defsystem #:logbot
67 :description "logbot"
68 :author "Michael Trinque <mike@trinque.org>"
69 :license "http://trilema.com/2015/a-new-software-licensing-paradigm/"
70 :depends-on (#:cl-irc
71 #:cl-postgres
72 #:ircbot
73 #:postmodern)
74 :components ((:file "package")
75 (:file "logbot")))
76
-(0 . 0)(1 . 93)
82 (in-package #:logbot)
83
84
85 (defun get-and-purge-outbox-messages (db)
86 (postmodern:with-connection db
87 (postmodern:query
88 "with deleted as (
89 delete from outbox
90 returning target, message, queued_at
91 )
92 select target,
93 message
94 from deleted
95 order by queued_at"
96 :rows)))
97
98 (defun make-log-entry (db target message host source user)
99 (postmodern:with-connection db
100 (postmodern:execute
101 "insert into log (target, message, host, source, \"user\")
102 values ($1, $2, $3, $4, $5)"
103 target
104 message
105 (if (string= "" host) :null host)
106 source
107 (if (null user) :null user))))
108
109
110 (defclass logbot (ircbot)
111 ((pg-thread :accessor logbot-pg-thread :initform nil)
112 (db :reader logbot-db :initarg :db)))
113
114 (defmethod ircbot-connect :after ((bot logbot))
115 (let ((conn (ircbot-connection bot)))
116 (add-hook conn 'irc-mode-message (lambda (message)
117 (logbot-check-mode bot message)))
118 (add-hook conn 'irc-privmsg-message (lambda (message)
119 (destructuring-bind (target message-text) (arguments message)
120 (make-log-entry (logbot-db bot)
121 target
122 message-text
123 (host message)
124 (source message)
125 (user message)))))))
126
127 (defmethod ircbot-send-message :after ((bot logbot) target message-text)
128 (let* ((b-connection (ircbot-connection bot))
129 (b-user (user b-connection)))
130 (make-log-entry (logbot-db bot)
131 target
132 message-text
133 (hostname b-user)
134 (nickname b-user)
135 (username b-user))))
136
137 (defmethod logbot-check-mode ((bot logbot) message)
138 (if (= 3 (length (arguments message)))
139 (destructuring-bind (channel mode nick) (arguments message)
140 (when (and (string= (host message) "services.")
141 (string= channel (ircbot-channel bot))
142 (or (string= mode "+o") (string= mode "+v"))
143 (string= nick (ircbot-nick bot)))
144
145 (when (null (logbot-pg-thread bot))
146 (logbot-start-pg-thread bot)
147 (logbot-send-outbox bot))))))
148
149 (defmethod logbot-send-outbox ((bot logbot))
150 (loop
151 for (target message)
152 in (get-and-purge-outbox-messages (logbot-db bot))
153 do (ircbot-send-message bot target message)))
154
155 (defmethod logbot-start-pg-thread ((bot logbot))
156 (setf (logbot-pg-thread bot)
157 (sb-thread:make-thread
158 (lambda ()
159 (postmodern:with-connection (logbot-db bot)
160 (postmodern:execute "listen outbox_new_message")
161 (loop
162 (if (string= (cl-postgres:wait-for-notification postmodern:*database*)
163 "outbox_new_message")
164 (logbot-send-outbox bot)))))
165 :name "logbot-pg")))
166
167 (defun make-logbot (server port nick password channel db)
168 (make-instance 'logbot
169 :server server
170 :port port
171 :nick nick
172 :password password
173 :channel channel
174 :db db))
-(0 . 0)(1 . 48)
179 set search_path = public;
180
181 create extension if not exists plpgsql;
182 create extension if not exists pgcrypto;
183 create extension if not exists "uuid-ossp";
184
185 create table log (
186 id uuid primary key default gen_random_uuid(),
187 target text not null,
188 message text not null,
189 host text,
190 source text not null,
191 "user" text,
192 received_at timestamp without time zone not null default (now() at time zone 'utc')
193 );
194
195 create index log_received_at on log (received_at);
196 create index log_target on log (target);
197 create index log_source on log (source);
198
199 create or replace function log_insert_notify () returns trigger as $$
200 begin
201 perform pg_notify('log_new_message', NEW.id::text);
202 return NEW;
203 end;
204 $$ language plpgsql;
205
206 create trigger log_insert_notify_trigger
207 after insert on log
208 for each row execute procedure log_insert_notify ();
209
210 create table outbox (
211 id serial primary key,
212 target text not null,
213 message text not null,
214 queued_at timestamp without time zone not null default (now() at time zone 'utc')
215 );
216
217 create or replace function outbox_insert_notify () returns trigger as $$
218 begin
219 perform pg_notify('outbox_new_message', NEW.id::text);
220 return NEW;
221 end;
222 $$ language plpgsql;
223
224 create trigger outbox_insert_notify_trigger
225 after insert on outbox
226 for each row execute procedure outbox_insert_notify ();
-(0 . 0)(1 . 20)
231 ;;;; package.lisp
232
233 (defpackage :logbot
234 (:use :cl
235 :cl-irc
236 :ircbot)
237 (:export :make-logbot
238 :logbot
239 :ircbot-connect
240 :ircbot-connect-thread
241 :ircbot-disconnect
242 :ircbot-reconnect
243 :ircbot-connection
244 :ircbot-channel
245 :ircbot-send-message
246 :ircbot-server
247 :ircbot-port
248 :ircbot-nick
249 :ircbot-lag))
250