While attempting to press trinque's ircbot including whaack's and ben_vulpes's patches, I ran into the issue of the genesis having been created using a differing hashing algorithm than that used for whaack's patch making pressing ircbot impossible using the available patches and genesis. Here you'll find a genesis including trinque's genesis and the changes from ben_vulpes's patch and whaack's patch.
logbot-genesis.vpatch
logbot-genesis.vpatch.asc
1 | diff -uNr a/ircbot/INSTALL b/ircbot/INSTALL |
2 | |
3 | |
4 | |
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 |
24 | diff -uNr a/ircbot/README b/ircbot/README |
25 | |
26 | |
27 | |
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 | + |
36 | diff -uNr a/ircbot/USAGE b/ircbot/USAGE |
37 | |
38 | |
39 | |
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 | + |
54 | diff -uNr a/ircbot/ircbot.asd b/ircbot/ircbot.asd |
55 | |
56 | |
57 | |
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 | + |
68 | diff -uNr a/ircbot/ircbot.lisp b/ircbot/ircbot.lisp |
69 | |
70 | |
71 | |
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 | + (channels :reader ircbot-channels :initarg :channels) |
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 | + (map nil |
117 | + (lambda (c) (join (ircbot-connection bot) c)) |
118 | + (ircbot-channels bot)))) |
119 | + (add-hook conn 'irc-notice-message (lambda (message) |
120 | + (ircbot-handle-nickserv bot message))) |
121 | + (add-hook conn 'irc-pong-message (lambda (message) |
122 | + (ircbot-handle-pong bot message))) |
123 | + (add-hook conn 'irc-rpl_welcome-message (lambda (message) |
124 | + (ircbot-start-ping-thread bot) |
125 | + (ircbot-check-nick bot message))))) |
126 | + |
127 | +(defmethod ircbot-connect-thread ((bot ircbot)) |
128 | + (setf (ircbot-run-thread bot) |
129 | + (sb-thread:make-thread (lambda () (ircbot-connect bot)) |
130 | + :name "ircbot-run"))) |
131 | + |
132 | +(defmethod ircbot-disconnect ((bot ircbot) &optional (quit-msg "...")) |
133 | + (sb-sys:without-interrupts |
134 | + (quit (ircbot-connection bot) quit-msg) |
135 | + (setf (ircbot-lag-track bot) nil) |
136 | + (setf (ircbot-connection bot) nil) |
137 | + (if (not (null (ircbot-run-thread bot))) |
138 | + (sb-thread:terminate-thread (ircbot-run-thread bot))) |
139 | + (if (not (or (null (ircbot-ping-thread bot)) (equal sb-thread:*current-thread* (ircbot-ping-thread bot)))) |
140 | + (sb-thread:terminate-thread (ircbot-ping-thread bot))))) |
141 | + |
142 | +(defmethod ircbot-reconnect ((bot ircbot) &optional (quit-msg "...")) |
143 | + (let ((threaded-p (not (null (ircbot-run-thread bot))))) |
144 | + (ircbot-disconnect bot quit-msg) |
145 | + (if threaded-p |
146 | + (ircbot-connect-thread bot) |
147 | + (ircbot-connect bot)))) |
148 | + |
149 | +(defmethod ircbot-handle-nickserv ((bot ircbot) message) |
150 | + (let ((conn (ircbot-connection bot))) |
151 | + (if (string= (host message) "services.") |
152 | + (destructuring-bind (target msgtext) (arguments message) |
153 | + (declare (ignore target)) |
154 | + (cond ((string= msgtext "This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify <password>.") |
155 | + (ircbot-nickserv-auth bot)) |
156 | + ((string= msgtext (format nil "~A has been ghosted." (ircbot-nick bot))) |
157 | + (nick conn (ircbot-nick bot))) |
158 | + ((string= msgtext (format nil "~A is not online." (ircbot-nick bot))) |
159 | + (ircbot-nickserv-auth bot)) |
160 | + ((string= msgtext (format nil "You are now identified for ~A." (ircbot-nick bot))) |
161 | + (map nil (lambda (c) (join conn c)) (ircbot-channels bot)))))))) |
162 | + |
163 | +(defmethod ircbot-handle-pong ((bot ircbot) message) |
164 | + (destructuring-bind (server ping) (arguments message) |
165 | + (declare (ignore server)) |
166 | + (let ((response (ignore-errors (parse-integer ping)))) |
167 | + (when response |
168 | + (setf (ircbot-lag-track bot) (delete response (ircbot-lag-track bot) :test #'=)) |
169 | + (setf (ircbot-lag bot) (- (received-time message) response)))))) |
170 | + |
171 | +(defmethod ircbot-nickserv-auth ((bot ircbot)) |
172 | + (privmsg (ircbot-connection bot) "NickServ" |
173 | + (format nil "identify ~A" (ircbot-password bot)))) |
174 | + |
175 | +(defmethod ircbot-nickserv-ghost ((bot ircbot)) |
176 | + (privmsg (ircbot-connection bot) "NickServ" |
177 | + (format nil "ghost ~A ~A" (ircbot-nick bot) (ircbot-password bot)))) |
178 | + |
179 | +(defmethod ircbot-randomize-nick ((bot ircbot)) |
180 | + (nick (ircbot-connection bot) |
181 | + (format nil "~A-~A" (ircbot-nick bot) (+ (random 90000) 10000)))) |
182 | + |
183 | +(defmethod ircbot-send-message ((bot ircbot) target message-text) |
184 | + (privmsg (ircbot-connection bot) target message-text)) |
185 | + |
186 | +(defmethod ircbot-start-ping-thread ((bot ircbot)) |
187 | + (let ((conn (ircbot-connection bot))) |
188 | + (setf (ircbot-ping-thread bot) |
189 | + (sb-thread:make-thread |
190 | + (lambda () |
191 | + (loop |
192 | + do (progn (sleep *ping-freq*) |
193 | + (let ((ct (get-universal-time))) |
194 | + (push ct (ircbot-lag-track bot)) |
195 | + (ping conn (princ-to-string ct)))) |
196 | + until (ircbot-timed-out-p bot)) |
197 | + (ircbot-reconnect bot)) |
198 | + :name "ircbot-ping")))) |
199 | + |
200 | +(defmethod ircbot-timed-out-p ((bot ircbot)) |
201 | + (loop |
202 | + with ct = (get-universal-time) |
203 | + for v in (ircbot-lag-track bot) |
204 | + when (> (- ct v) *max-lag*) |
205 | + do (return t))) |
206 | + |
207 | + |
208 | +(defun make-ircbot (server port nick password channels) |
209 | + (make-instance 'ircbot |
210 | + :server server |
211 | + :port port |
212 | + :nick nick |
213 | + :password password |
214 | + :channels channels)) |
215 | diff -uNr a/ircbot/manifest b/ircbot/manifest |
216 | |
217 | |
218 | |
219 | +658020 ircbot_genesis thimbronion This genesis combines trinque's genesis with ben_vulpes' multi-channel fix and whaack's reconnection fix. Theses patches were generated using differing hash algos and can not be applied by any existing v implementation and I do not see any reason that anyone would find ircbot usable without any of them. |
220 | diff -uNr a/ircbot/package.lisp b/ircbot/package.lisp |
221 | |
222 | |
223 | |
224 | +;;;; package.lisp |
225 | + |
226 | +(defpackage :ircbot |
227 | + (:use :cl |
228 | + :cl-irc) |
229 | + (:export :make-ircbot |
230 | + :ircbot |
231 | + :ircbot-connect |
232 | + :ircbot-connect-thread |
233 | + :ircbot-disconnect |
234 | + :ircbot-reconnect |
235 | + :ircbot-connection |
236 | + :ircbot-channels |
237 | + :ircbot-send-message |
238 | + :ircbot-server |
239 | + :ircbot-port |
240 | + :ircbot-nick |
241 | + :ircbot-lag)) |
242 | / |
Thanks for doing this. For practical use, one may want to add a handler case in the reconnect function that catches network related errors. The handler case should recall the reconnect function; this way the reconnect function keeps trying while the network is down rather than just failing once and giving up. I'll post the relevant code when I write it for my block explorer.