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.