diff -uNr a/trilemabot/INSTALL b/trilemabot/INSTALL --- a/trilemabot/INSTALL false +++ b/trilemabot/INSTALL 82ad36738701274f72deb3647d314b58594f5303234b94d3a43ab86cd7ebb7eb20347e10cf3efae693e3dd0584fa68faf2c693f0a365e0a741a7eb1be897fd94 @@ -0,0 +1,22 @@ +INSTALL + + * Install and load cl-irc and its dependencies + + * Install ircbot + + * Use V to press `trilemabot` + +mkdir -p ~/src/trilemabot +cd ~/src/trilemabot + +mkdir .wot +cd .wot && wget http://lucian.mogosanu.ro/spyked.asc && cd .. + +v.pl init http://lucian.mogosanu.ro/src/trilemabot/ +v.pl press trilemabot-voicer trilemabot-voicer.vpatch + + * Load `trilemabot` via Quicklisp or directly using ASDF: + +(dolist (path '("/path/to/ircbot/" "~/src/trilemabot/trilemabot-voicer/")) + (pushnew path asdf:*central-registry* :test #'string=)) +(asdf:load-system :trilemabot) diff -uNr a/trilemabot/README b/trilemabot/README --- a/trilemabot/README false +++ b/trilemabot/README c19e90ea25aa5f0f7ab9a050ffa70ce8d0c3c3bea592e9efadbe062d45c76283d0daeaea925c08d721635b5f5c86a2d675e60126490075d178cd4928aa769fdb @@ -0,0 +1,12 @@ +README + +`trilemabot` extends `ircbot` with #trilema-specific functionality. See the +following for more details: + + * http://trilema.com/2016/how-to-participate-in-the-affairs-of-the-most-serene-republic/ + * http://trilema.com/2016/trilema-bot-spec/ + * http://thetarpit.org/posts/y04/072-trilemabot-i.html + +Currently the following functionalities are implemented: + + * self-voicing with deedbot, using a list of pre-provided OTPs diff -uNr a/trilemabot/USAGE b/trilemabot/USAGE --- a/trilemabot/USAGE false +++ b/trilemabot/USAGE 8739f7eec64fe69aa88360214528cbc658c20a47af41309c5c054f89fb66d03a401bfa3e1565bb26a21bb1c1ca6ac168692a02499a81b8a1fee4401622a65fd3 @@ -0,0 +1,37 @@ +USAGE + +(asdf:load-system :trilemabot) +(defvar *bot*) +(setq *bot* + (trlb:make-trilemabot + "chat.freenode.net" 6667 "nick" "password" '("#trilema"))) + +;; connect in separate thread, returning thread +(ircbot:ircbot-connect-thread *bot*) + +;;;; 1. Self-voicing + +;; ask for n !!up OTPs from deedbot +(loop for i from 1 to n do + (trlb:trilemabot-send-up *bot*) + (sleep 0.5)) + +;; get messages received from deedbot +(trlb:trilemabot-inbox *bot*) + +;; add decrypted !!up OTPs +(trlb:trilemabot-add-voice-otps *bot* + "decrypted-otp-1" "decrypted-otp2" ... "decrypted-otpn") + +;; get the list of !!up OTPs +(trlb:trilemabot-voice-otp-stash *bot*) + +;; save !!up OTPs to disk +(trlb:trilemabot-save-voice-otp-stash *bot* "voice-otps.sexp") + +;; self-voice +(trlb:trilemabot-voice *bot*) + +;; if `voice-otp-stash' is not empty, the bot will automatically +;; self-voice on reconnect +(ircbot:ircbot-reconnect *bot*) diff -uNr a/trilemabot/package.lisp b/trilemabot/package.lisp --- a/trilemabot/package.lisp false +++ b/trilemabot/package.lisp 6373f3f4fad674c92bbd9a10c81fcf63444d7e4763c5bd7af1c17b0cf42d311136f801ae257b81a5e6f50be21b43372c0fdfd52d8d72bced002a586e793875ce @@ -0,0 +1,19 @@ +;;;; package.lisp + +(in-package #:cl-user) + +(defpackage #:trilemabot + (:use #:cl #:cl-irc #:ircbot) + (:nicknames #:trlb) + (:export #:trilemabot + #:trilemabot-voice-otp-stash + #:trilemabot-inbox + #:trilemabot-join-channel + #:trilemabot-part-channel + #:trilemabot-handle-privmsg + #:trilemabot-check-mode + #:trilemabot-voice + #:trilemabot-add-voice-otps + #:trilemabot-send-up + #:trilemabot-send-otp + #:trilemabot-save-voice-otp-stash)) diff -uNr a/trilemabot/trilemabot.asd b/trilemabot/trilemabot.asd --- a/trilemabot/trilemabot.asd false +++ b/trilemabot/trilemabot.asd 19179fc46c4a82b0f658b76c7632cb46253f158a8487d4cd2d2172c754349e93c89367f2bb8bc6490a56cf5a92d876031267c11d910925267ec495118daaf4f9 @@ -0,0 +1,10 @@ +;;;; trilemabot.asd + +(asdf:defsystem #:trilemabot + :description "trilemabot" + :author "Lucian Mogosanu " + :license "http://trilema.com/2015/a-new-software-licensing-paradigm/" + :depends-on (#:cl-irc + #:ircbot) + :components ((:file "package") + (:file "trilemabot"))) diff -uNr a/trilemabot/trilemabot.lisp b/trilemabot/trilemabot.lisp --- a/trilemabot/trilemabot.lisp false +++ b/trilemabot/trilemabot.lisp dc58fde859b31b9e12dab60bb7631955bd12f890b04a8f09bbf170512ee4a0c8a37f692ce419c8d56a4aa5017496c699b506c71610c9ad594b3b22312c298c03 @@ -0,0 +1,120 @@ +;;;; trilemabot.lisp + +(in-package #:trilemabot) + +;; These are used to check that we're joining #trilema; make sure that +;; your bot is configured accordingly if you want to use +;; #trilema-specific functionality. +(defparameter *trilema-server* ".freenode.net") +(defparameter *trilema-channel* "#trilema") +(defparameter *trilema-deedbot-nick* "deedbot") + +(defclass trilemabot (ircbot) + ((in-chan :accessor trilemabot-in-chan :initform nil) + (voiced :accessor trilemabot-voiced :initform nil) + (voice-otp-stash :accessor trilemabot-voice-otp-stash + :initarg :voice-otp-stash + :initform nil) + (inbox :accessor trilemabot-inbox + :initform nil))) + +(defmethod ircbot-connect :after ((bot trilemabot)) + (with-slots (connection) bot + (add-hook connection 'irc-join-message + #'(lambda (message) (trilemabot-join-channel bot message))) + (add-hook connection 'irc-part-message + #'(lambda (message) (trilemabot-part-channel bot message))) + (add-hook connection 'irc-privmsg-message + #'(lambda (message) (trilemabot-handle-privmsg bot message))) + (add-hook connection 'irc-mode-message + #'(lambda (message) (trilemabot-check-mode bot message))))) + +(defmethod ircbot-disconnect :after ((bot trilemabot) &optional (quit-msg "")) + (declare (ignore quit-msg)) + (with-slots (in-chan voiced) bot + (setf in-chan nil + voiced nil))) + +(defmethod trilemabot-join-channel ((bot trilemabot) message) + (destructuring-bind (channel) (arguments message) + (when (and (string= (source message) (ircbot-nick bot)) + (string= channel *trilema-channel*) + (search *trilema-server* (ircbot-server bot))) + (setf (trilemabot-in-chan bot) t) + (trilemabot-voice bot)))) + +(defmethod trilemabot-part-channel ((bot trilemabot) message) + (let ((channel (car (arguments message)))) + (when (and (string= (source message) (ircbot-nick bot)) + (string= channel *trilema-channel*) + (search *trilema-server* (ircbot-server bot))) + (setf (trilemabot-in-chan bot) nil + (trilemabot-voiced bot) nil)))) + +(defmethod trilemabot-handle-privmsg ((bot trilemabot) message) + (destructuring-bind (target message-text) (arguments message) + (when (and (string= (source message) *trilema-deedbot-nick*) + (string= target (ircbot-nick bot))) + (format *standard-output* "<~a>: ~a~%" + (source message) message-text) + (push (list :from (source message) + :time (received-time message) + :message message-text) + (trilemabot-inbox bot))))) + +(defmethod trilemabot-check-mode ((bot trilemabot) message) + (when (= 3 (length (arguments message))) ; mode change for user in chan + (destructuring-bind (channel mode nick) (arguments message) + (when (and (string= channel *trilema-channel*) + (string= nick (ircbot-nick bot))) + (cond + ((or (string= mode "+o") + (string= mode "+v")) (setf (trilemabot-voiced bot) t)) + ((or (string= mode "-o") + (string= mode "-v")) (setf (trilemabot-voiced bot) nil))))))) + +(defmethod trilemabot-add-voice-otps ((bot trilemabot) &rest otps) + (with-slots (voice-otp-stash) bot + (setf voice-otp-stash (nconc voice-otp-stash otps)))) + +(defmethod trilemabot-voice ((bot trilemabot)) + (with-slots (voice-otp-stash) bot + (if voice-otp-stash + (trilemabot-send-otp bot (pop voice-otp-stash)) + (format *standard-output* "[trilemabot ~a@~a] No OTPs available.~%" + (ircbot-nick bot) (ircbot-server bot))))) + +(defmethod trilemabot-send-up ((bot trilemabot) &optional nick) + (ircbot-send-message bot *trilema-deedbot-nick* + (make-bot-command "!!up" + (or nick (ircbot-nick bot))))) + +(defmethod trilemabot-send-otp ((bot trilemabot) otp) + (ircbot-send-message bot *trilema-deedbot-nick* + (make-bot-command "!!v" otp))) + +(defun make-bot-command (command &rest arguments) + "Make raw bot command string consisting of `command' and +space-separated `arguments'." + (with-output-to-string (out) + (write-string command out) + (dolist (arg arguments) + (write-string " " out) + (write-string arg out)))) + +(defmethod trilemabot-save-voice-otp-stash ((bot trilemabot) filespec) + (with-open-file (out filespec + :direction :output + :if-exists :supersede + :if-does-not-exist :create) + (print (trlb:trilemabot-voice-otp-stash bot) out) + (finish-output out))) + +(defun make-trilemabot (server port nick password channels &optional voice-otps) + (make-instance 'trilemabot + :server server + :port port + :nick nick + :password password + :channels channels + :voice-otp-stash voice-otps))