tree checksum vpatch file split hunks
all signers: trinque
antecedents:
press order:
ircbot-genesis | trinque |
patch:
(0 . 0)(1 . 19)
5 INSTALL
6
7 * Install SBCL (with sb-thread) and Quicklisp.
8
9 * From the SBCL REPL:
10 (ql:quickload :cl-irc)
11
12 * Use V to press `ircbot`
13
14 mkdir -p ~/src/ircbot
15 cd ~/src/ircbot
16
17 mkdir .wot
18 cd .wot && wget http://trinque.org/trinque.asc && cd ..
19
20 v.pl init http://trinque.org/src/ircbot
21 v.pl press ircbot-genesis ircbot-genesis.vpatch
22
23 ln -s ~/src/ircbot/ircbot-genesis ~/quicklisp/local-projects/ircbot
-(0 . 0)(1 . 8)
28 README
29
30 `ircbot` provides a simple CLOS class, `ircbot`, which will maintain a
31 connection to a single IRC channel via `cl-irc`. The bot will handle
32 ping/pong and detect failed connections, and is capable of
33 authenticating with NickServ (using ghost when necessary to
34 reacquire nick).
35
-(0 . 0)(1 . 14)
40 USAGE
41
42 (asdf:load-system :ircbot)
43 (defvar *bot*)
44 (setf *bot*
45 (ircbot:make-ircbot
46 "chat.freenode.net" 6667 "nick" "password" "#channel"))
47
48 ; connect in separate thread, returning thread
49 (ircbot:ircbot-connect-thread *bot*)
50
51 ; or connect using the current thread
52 ; (ircbot:ircbot-connect *bot*)
53
-(0 . 0)(1 . 10)
58 ;;;; ircbot.asd
59
60 (asdf:defsystem #:ircbot
61 :description "ircbot"
62 :author "Michael Trinque <mike@trinque.org>"
63 :license "http://trilema.com/2015/a-new-software-licensing-paradigm/"
64 :depends-on (#:cl-irc)
65 :components ((:file "package")
66 (:file "ircbot")))
67
-(0 . 0)(1 . 141)
72 (in-package #:ircbot)
73
74 (defvar *max-lag* 60)
75 (defvar *ping-freq* 30)
76
77
78 (defclass ircbot ()
79 ((connection :accessor ircbot-connection :initform nil)
80 (channel :reader ircbot-channel :initarg :channel)
81 (server :reader ircbot-server :initarg :server)
82 (port :reader ircbot-port :initarg :port)
83 (nick :reader ircbot-nick :initarg :nick)
84 (password :reader ircbot-password :initarg :password)
85 (connection-security :reader ircbot-connection-security
86 :initarg :connection-security
87 :initform :none)
88 (run-thread :accessor ircbot-run-thread :initform nil)
89 (ping-thread :accessor ircbot-ping-thread :initform nil)
90 (lag :accessor ircbot-lag :initform nil)
91 (lag-track :accessor ircbot-lag-track :initform nil)))
92
93 (defmethod ircbot-check-nick ((bot ircbot) message)
94 (destructuring-bind (target msgtext) (arguments message)
95 (declare (ignore msgtext))
96 (if (string= target (ircbot-nick bot))
97 (ircbot-nickserv-auth bot)
98 (ircbot-nickserv-ghost bot))))
99
100 (defmethod ircbot-connect :around ((bot ircbot))
101 (let ((conn (connect :nickname (ircbot-nick bot)
102 :server (ircbot-server bot)
103 :port (ircbot-port bot)
104 :connection-security (ircbot-connection-security bot))))
105 (setf (ircbot-connection bot) conn)
106 (call-next-method)
107 (read-message-loop conn)))
108
109 (defmethod ircbot-connect ((bot ircbot))
110 (let ((conn (ircbot-connection bot)))
111 (add-hook conn 'irc-err_nicknameinuse-message (lambda (message)
112 (declare (ignore message))
113 (ircbot-randomize-nick bot)))
114 (add-hook conn 'irc-kick-message (lambda (message)
115 (declare (ignore message))
116 (join (ircbot-connection bot)
117 (ircbot-channel bot))))
118 (add-hook conn 'irc-notice-message (lambda (message)
119 (ircbot-handle-nickserv bot message)))
120 (add-hook conn 'irc-pong-message (lambda (message)
121 (ircbot-handle-pong bot message)))
122 (add-hook conn 'irc-rpl_welcome-message (lambda (message)
123 (ircbot-start-ping-thread bot)
124 (ircbot-check-nick bot message)))))
125
126 (defmethod ircbot-connect-thread ((bot ircbot))
127 (setf (ircbot-run-thread bot)
128 (sb-thread:make-thread (lambda () (ircbot-connect bot))
129 :name "ircbot-run")))
130
131 (defmethod ircbot-disconnect ((bot ircbot) &optional (quit-msg "..."))
132 (sb-sys:without-interrupts
133 (quit (ircbot-connection bot) quit-msg)
134 (setf (ircbot-lag-track bot) nil)
135 (setf (ircbot-connection bot) nil)
136 (if (not (null (ircbot-run-thread bot)))
137 (sb-thread:terminate-thread (ircbot-run-thread bot)))
138 (sb-thread:terminate-thread (ircbot-ping-thread bot))))
139
140 (defmethod ircbot-reconnect ((bot ircbot) &optional (quit-msg "..."))
141 (let ((threaded-p (not (null (ircbot-run-thread bot)))))
142 (ircbot-disconnect bot quit-msg)
143 (if threaded-p
144 (ircbot-connect-thread bot)
145 (ircbot-connect bot))))
146
147 (defmethod ircbot-handle-nickserv ((bot ircbot) message)
148 (let ((conn (ircbot-connection bot)))
149 (if (string= (host message) "services.")
150 (destructuring-bind (target msgtext) (arguments message)
151 (declare (ignore target))
152 (cond ((string= msgtext "This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify <password>.")
153 (ircbot-nickserv-auth bot))
154 ((string= msgtext (format nil "~A has been ghosted." (ircbot-nick bot)))
155 (nick conn (ircbot-nick bot)))
156 ((string= msgtext (format nil "~A is not online." (ircbot-nick bot)))
157 (ircbot-nickserv-auth bot))
158 ((string= msgtext (format nil "You are now identified for ~A." (ircbot-nick bot)))
159 (join conn (ircbot-channel bot))))))))
160
161 (defmethod ircbot-handle-pong ((bot ircbot) message)
162 (destructuring-bind (server ping) (arguments message)
163 (declare (ignore server))
164 (let ((response (ignore-errors (parse-integer ping))))
165 (when response
166 (setf (ircbot-lag-track bot) (delete response (ircbot-lag-track bot) :test #'=))
167 (setf (ircbot-lag bot) (- (received-time message) response))))))
168
169 (defmethod ircbot-nickserv-auth ((bot ircbot))
170 (privmsg (ircbot-connection bot) "NickServ"
171 (format nil "identify ~A" (ircbot-password bot))))
172
173 (defmethod ircbot-nickserv-ghost ((bot ircbot))
174 (privmsg (ircbot-connection bot) "NickServ"
175 (format nil "ghost ~A ~A" (ircbot-nick bot) (ircbot-password bot))))
176
177 (defmethod ircbot-randomize-nick ((bot ircbot))
178 (nick (ircbot-connection bot)
179 (format nil "~A-~A" (ircbot-nick bot) (+ (random 90000) 10000))))
180
181 (defmethod ircbot-send-message ((bot ircbot) target message-text)
182 (privmsg (ircbot-connection bot) target message-text))
183
184 (defmethod ircbot-start-ping-thread ((bot ircbot))
185 (let ((conn (ircbot-connection bot)))
186 (setf (ircbot-ping-thread bot)
187 (sb-thread:make-thread
188 (lambda ()
189 (loop
190 do (progn (sleep *ping-freq*)
191 (let ((ct (get-universal-time)))
192 (push ct (ircbot-lag-track bot))
193 (ping conn (princ-to-string ct))))
194 until (ircbot-timed-out-p bot))
195 (ircbot-reconnect bot))
196 :name "ircbot-ping"))))
197
198 (defmethod ircbot-timed-out-p ((bot ircbot))
199 (loop
200 with ct = (get-universal-time)
201 for v in (ircbot-lag-track bot)
202 when (> (- ct v) *max-lag*)
203 do (return t)))
204
205
206 (defun make-ircbot (server port nick password channel)
207 (make-instance 'ircbot
208 :server server
209 :port port
210 :nick nick
211 :password password
212 :channel channel))
-(0 . 0)(1 . 19)
217 ;;;; package.lisp
218
219 (defpackage :ircbot
220 (:use :cl
221 :cl-irc)
222 (:export :make-ircbot
223 :ircbot
224 :ircbot-connect
225 :ircbot-connect-thread
226 :ircbot-disconnect
227 :ircbot-reconnect
228 :ircbot-connection
229 :ircbot-channel
230 :ircbot-send-message
231 :ircbot-server
232 :ircbot-port
233 :ircbot-nick
234 :ircbot-lag))
235