| 1 |
diff -uNr a/ircbot/INSTALL b/ircbot/INSTALL |
| 2 |
--- a/ircbot/INSTALL false |
| 3 |
+++ b/ircbot/INSTALL c4c41d96ddb71db32e8cd54c22e7250abbc52d51f4b25d8092dc094b4a84100949d0a74378fc33131d2d9a5144156a738f5a91d7e949922898993eb4384b4757 |
| 4 |
@@ -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 |
| 24 |
diff -uNr a/ircbot/README b/ircbot/README |
| 25 |
--- a/ircbot/README false |
| 26 |
+++ b/ircbot/README 6a76028622c6bb986d68d42b7b133221d3659d56da1bd5d4e8b39f0a6075a17d8b3ee33c8e37c7d4b3ec1f108ad940777d762bab01763a63742733a1445660d4 |
| 27 |
@@ -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 |
+ |
| 36 |
diff -uNr a/ircbot/USAGE b/ircbot/USAGE |
| 37 |
--- a/ircbot/USAGE false |
| 38 |
+++ b/ircbot/USAGE 20d07e6f190f6655a2884e60c1d6a7eccdd76d019797bb165c716a6919fb5161a31b1956b9dda7927839837a924ae6ea3d0c1a833458bbbd5765f76548d637d2 |
| 39 |
@@ -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 |
+ |
| 54 |
diff -uNr a/ircbot/ircbot.asd b/ircbot/ircbot.asd |
| 55 |
--- a/ircbot/ircbot.asd false |
| 56 |
+++ b/ircbot/ircbot.asd 9dfba5c2bd97e5ffc2ab071786b14c05dfda1c898ef58a5d87ea020bde042bf76c0e88b78f6d4a4fbd453aabea58e4710bf717defa023dfe410d19ac01c4e2d9 |
| 57 |
@@ -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 |
+ |
| 68 |
diff -uNr a/ircbot/ircbot.lisp b/ircbot/ircbot.lisp |
| 69 |
--- a/ircbot/ircbot.lisp false |
| 70 |
+++ b/ircbot/ircbot.lisp 738a2c0ca77a69fc7805cbfc668da1b61e25e512d6d9f3bdf200968e39eb201bb87be83c6ae1411c6e6a5c7dd63524a5b5ab71d99a2813ac85fc5ac4360b3b17 |
| 71 |
@@ -0,0 +1,143 @@ |
| 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 |
--- a/ircbot/manifest false |
| 217 |
+++ b/ircbot/manifest 8a535c4a26e5fba0aa52c44bfcd84176de82568ddd7e98e8fb84ab48b5dbc0bc315c09f37c8eb7201a88fb804a18712d1a876f02e06552157ebefc63a123a9c4 |
| 218 |
@@ -0,0 +1 @@ |
| 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 |
--- a/ircbot/package.lisp false |
| 222 |
+++ b/ircbot/package.lisp d186f3af63443337d23a0bfbaae79246fae2b2781acb53109132b42f84cf46acabf1fe12f2aba00c452e679c721ca955daaf302e1a04a56fccb8125d95e1527c |
| 223 |
@@ -0,0 +1,18 @@ |
| 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 |
/ |