diff -uNr a/ircbot/INSTALL b/ircbot/INSTALL --- a/ircbot/INSTALL false +++ b/ircbot/INSTALL c4c41d96ddb71db32e8cd54c22e7250abbc52d51f4b25d8092dc094b4a84100949d0a74378fc33131d2d9a5144156a738f5a91d7e949922898993eb4384b4757 @@ -0,0 +1,19 @@ +INSTALL + + * Install SBCL (with sb-thread) and Quicklisp. + + * From the SBCL REPL: + (ql:quickload :cl-irc) + + * Use V to press `ircbot` + +mkdir -p ~/src/ircbot +cd ~/src/ircbot + +mkdir .wot +cd .wot && wget http://trinque.org/trinque.asc && cd .. + +v.pl init http://trinque.org/src/ircbot +v.pl press ircbot-genesis ircbot-genesis.vpatch + +ln -s ~/src/ircbot/ircbot-genesis ~/quicklisp/local-projects/ircbot diff -uNr a/ircbot/README b/ircbot/README --- a/ircbot/README false +++ b/ircbot/README 6a76028622c6bb986d68d42b7b133221d3659d56da1bd5d4e8b39f0a6075a17d8b3ee33c8e37c7d4b3ec1f108ad940777d762bab01763a63742733a1445660d4 @@ -0,0 +1,8 @@ +README + +`ircbot` provides a simple CLOS class, `ircbot`, which will maintain a +connection to a single IRC channel via `cl-irc`. The bot will handle +ping/pong and detect failed connections, and is capable of +authenticating with NickServ (using ghost when necessary to +reacquire nick). + diff -uNr a/ircbot/USAGE b/ircbot/USAGE --- a/ircbot/USAGE false +++ b/ircbot/USAGE 20d07e6f190f6655a2884e60c1d6a7eccdd76d019797bb165c716a6919fb5161a31b1956b9dda7927839837a924ae6ea3d0c1a833458bbbd5765f76548d637d2 @@ -0,0 +1,14 @@ +USAGE + +(asdf:load-system :ircbot) +(defvar *bot*) +(setf *bot* + (ircbot:make-ircbot + "chat.freenode.net" 6667 "nick" "password" "#channel")) + +; connect in separate thread, returning thread +(ircbot:ircbot-connect-thread *bot*) + +; or connect using the current thread +; (ircbot:ircbot-connect *bot*) + diff -uNr a/ircbot/ircbot.asd b/ircbot/ircbot.asd --- a/ircbot/ircbot.asd false +++ b/ircbot/ircbot.asd 9dfba5c2bd97e5ffc2ab071786b14c05dfda1c898ef58a5d87ea020bde042bf76c0e88b78f6d4a4fbd453aabea58e4710bf717defa023dfe410d19ac01c4e2d9 @@ -0,0 +1,10 @@ +;;;; ircbot.asd + +(asdf:defsystem #:ircbot + :description "ircbot" + :author "Michael Trinque " + :license "http://trilema.com/2015/a-new-software-licensing-paradigm/" + :depends-on (#:cl-irc) + :components ((:file "package") + (:file "ircbot"))) + diff -uNr a/ircbot/ircbot.lisp b/ircbot/ircbot.lisp --- a/ircbot/ircbot.lisp false +++ b/ircbot/ircbot.lisp 738a2c0ca77a69fc7805cbfc668da1b61e25e512d6d9f3bdf200968e39eb201bb87be83c6ae1411c6e6a5c7dd63524a5b5ab71d99a2813ac85fc5ac4360b3b17 @@ -0,0 +1,143 @@ +(in-package #:ircbot) + +(defvar *max-lag* 60) +(defvar *ping-freq* 30) + + +(defclass ircbot () + ((connection :accessor ircbot-connection :initform nil) + (channels :reader ircbot-channels :initarg :channels) + (server :reader ircbot-server :initarg :server) + (port :reader ircbot-port :initarg :port) + (nick :reader ircbot-nick :initarg :nick) + (password :reader ircbot-password :initarg :password) + (connection-security :reader ircbot-connection-security + :initarg :connection-security + :initform :none) + (run-thread :accessor ircbot-run-thread :initform nil) + (ping-thread :accessor ircbot-ping-thread :initform nil) + (lag :accessor ircbot-lag :initform nil) + (lag-track :accessor ircbot-lag-track :initform nil))) + +(defmethod ircbot-check-nick ((bot ircbot) message) + (destructuring-bind (target msgtext) (arguments message) + (declare (ignore msgtext)) + (if (string= target (ircbot-nick bot)) + (ircbot-nickserv-auth bot) + (ircbot-nickserv-ghost bot)))) + +(defmethod ircbot-connect :around ((bot ircbot)) + (let ((conn (connect :nickname (ircbot-nick bot) + :server (ircbot-server bot) + :port (ircbot-port bot) + :connection-security (ircbot-connection-security bot)))) + (setf (ircbot-connection bot) conn) + (call-next-method) + (read-message-loop conn))) + +(defmethod ircbot-connect ((bot ircbot)) + (let ((conn (ircbot-connection bot))) + (add-hook conn 'irc-err_nicknameinuse-message (lambda (message) + (declare (ignore message)) + (ircbot-randomize-nick bot))) + (add-hook conn 'irc-kick-message (lambda (message) + (declare (ignore message)) + (map nil + (lambda (c) (join (ircbot-connection bot) c)) + (ircbot-channels bot)))) + (add-hook conn 'irc-notice-message (lambda (message) + (ircbot-handle-nickserv bot message))) + (add-hook conn 'irc-pong-message (lambda (message) + (ircbot-handle-pong bot message))) + (add-hook conn 'irc-rpl_welcome-message (lambda (message) + (ircbot-start-ping-thread bot) + (ircbot-check-nick bot message))))) + +(defmethod ircbot-connect-thread ((bot ircbot)) + (setf (ircbot-run-thread bot) + (sb-thread:make-thread (lambda () (ircbot-connect bot)) + :name "ircbot-run"))) + +(defmethod ircbot-disconnect ((bot ircbot) &optional (quit-msg "...")) + (sb-sys:without-interrupts + (quit (ircbot-connection bot) quit-msg) + (setf (ircbot-lag-track bot) nil) + (setf (ircbot-connection bot) nil) + (if (not (null (ircbot-run-thread bot))) + (sb-thread:terminate-thread (ircbot-run-thread bot))) + (if (not (or (null (ircbot-ping-thread bot)) (equal sb-thread:*current-thread* (ircbot-ping-thread bot)))) + (sb-thread:terminate-thread (ircbot-ping-thread bot))))) + +(defmethod ircbot-reconnect ((bot ircbot) &optional (quit-msg "...")) + (let ((threaded-p (not (null (ircbot-run-thread bot))))) + (ircbot-disconnect bot quit-msg) + (if threaded-p + (ircbot-connect-thread bot) + (ircbot-connect bot)))) + +(defmethod ircbot-handle-nickserv ((bot ircbot) message) + (let ((conn (ircbot-connection bot))) + (if (string= (host message) "services.") + (destructuring-bind (target msgtext) (arguments message) + (declare (ignore target)) + (cond ((string= msgtext "This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify .") + (ircbot-nickserv-auth bot)) + ((string= msgtext (format nil "~A has been ghosted." (ircbot-nick bot))) + (nick conn (ircbot-nick bot))) + ((string= msgtext (format nil "~A is not online." (ircbot-nick bot))) + (ircbot-nickserv-auth bot)) + ((string= msgtext (format nil "You are now identified for ~A." (ircbot-nick bot))) + (map nil (lambda (c) (join conn c)) (ircbot-channels bot)))))))) + +(defmethod ircbot-handle-pong ((bot ircbot) message) + (destructuring-bind (server ping) (arguments message) + (declare (ignore server)) + (let ((response (ignore-errors (parse-integer ping)))) + (when response + (setf (ircbot-lag-track bot) (delete response (ircbot-lag-track bot) :test #'=)) + (setf (ircbot-lag bot) (- (received-time message) response)))))) + +(defmethod ircbot-nickserv-auth ((bot ircbot)) + (privmsg (ircbot-connection bot) "NickServ" + (format nil "identify ~A" (ircbot-password bot)))) + +(defmethod ircbot-nickserv-ghost ((bot ircbot)) + (privmsg (ircbot-connection bot) "NickServ" + (format nil "ghost ~A ~A" (ircbot-nick bot) (ircbot-password bot)))) + +(defmethod ircbot-randomize-nick ((bot ircbot)) + (nick (ircbot-connection bot) + (format nil "~A-~A" (ircbot-nick bot) (+ (random 90000) 10000)))) + +(defmethod ircbot-send-message ((bot ircbot) target message-text) + (privmsg (ircbot-connection bot) target message-text)) + +(defmethod ircbot-start-ping-thread ((bot ircbot)) + (let ((conn (ircbot-connection bot))) + (setf (ircbot-ping-thread bot) + (sb-thread:make-thread + (lambda () + (loop + do (progn (sleep *ping-freq*) + (let ((ct (get-universal-time))) + (push ct (ircbot-lag-track bot)) + (ping conn (princ-to-string ct)))) + until (ircbot-timed-out-p bot)) + (ircbot-reconnect bot)) + :name "ircbot-ping")))) + +(defmethod ircbot-timed-out-p ((bot ircbot)) + (loop + with ct = (get-universal-time) + for v in (ircbot-lag-track bot) + when (> (- ct v) *max-lag*) + do (return t))) + + +(defun make-ircbot (server port nick password channels) + (make-instance 'ircbot + :server server + :port port + :nick nick + :password password + :channels channels)) diff -uNr a/ircbot/manifest b/ircbot/manifest --- a/ircbot/manifest false +++ b/ircbot/manifest 8a535c4a26e5fba0aa52c44bfcd84176de82568ddd7e98e8fb84ab48b5dbc0bc315c09f37c8eb7201a88fb804a18712d1a876f02e06552157ebefc63a123a9c4 @@ -0,0 +1 @@ +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. diff -uNr a/ircbot/package.lisp b/ircbot/package.lisp --- a/ircbot/package.lisp false +++ b/ircbot/package.lisp d186f3af63443337d23a0bfbaae79246fae2b2781acb53109132b42f84cf46acabf1fe12f2aba00c452e679c721ca955daaf302e1a04a56fccb8125d95e1527c @@ -0,0 +1,18 @@ +;;;; package.lisp + +(defpackage :ircbot + (:use :cl + :cl-irc) + (:export :make-ircbot + :ircbot + :ircbot-connect + :ircbot-connect-thread + :ircbot-disconnect + :ircbot-reconnect + :ircbot-connection + :ircbot-channels + :ircbot-send-message + :ircbot-server + :ircbot-port + :ircbot-nick + :ircbot-lag))