1 ;; Botbot: Very basic IRC bot
8 (chicken process-context)
10 matchable srfi-13 srfi-1 srfi-18
11 uri-common tcp6 openssl)
18 (define bot-channel #f)
19 (define bot-proc-file #f)
24 (define ping-period 60) ;seconds
26 (tcp-read-timeout #f) ;disable read timeout
29 ;; (let-values (((in-port out-port) (tcp-connect host port)))
30 (set! bot-proc (eval (with-input-from-file bot-proc-file read)))
31 (let-values (((in-port out-port)
33 (ssl-connect* hostname: irc-host port: (or irc-port 6697))
34 (tcp-connect irc-host (or irc-port 6667)))))
36 (if (establish-connection in-port out-port)
37 ;; (bot-loop in-port out-port)
39 (print "Successfully connected!")
40 (start-ping-timer out-port)
41 (bot-loop in-port out-port))
42 (print "Failed to establish connection. Aborting..."))))
44 (define (establish-connection in-port out-port)
45 (write-msg `(#f #f "NICK" (,bot-nick)) out-port)
46 (write-msg `(#f #f "USER" (,bot-nick "0" "*" ,bot-nick)) out-port)
48 (write-msg `(#f #f "JOIN" (,bot-channel)) out-port))
51 (define (start-ping-timer out-port)
55 (thread-sleep! ping-period)
56 (write-msg `(#f #f "PING" (,bot-host)) out-port) ; send ping
59 (define (bot-loop in-port out-port)
60 (let ((privmsg (lambda (to . args)
61 (write-msg (list #f #f "PRIVMSG" (cons to args)) out-port))))
62 (let loop ((msg (read-msg in-port)))
63 (match (cons (msg-source msg) (cons (msg-command msg) (msg-args msg)))
65 (write-msg `(#f #f "PONG" (,token)) out-port))
66 ((source "PRIVMSG" target args ...)
67 (when (string=? target bot-nick)
68 (bot-proc source args privmsg)))
72 (loop (read-msg in-port)))))
74 (define (read-msg in-port)
75 (let ((msg (string->msg (read-line in-port))))
76 (display "Received message: ")
81 (define (write-msg msg out-port)
82 (with-output-to-port out-port
83 (lambda () (write-string (conc (msg->string msg) "\r\n"))))
84 (print "Sent message: " msg))
88 (? (: "@" (submatch (+ (~ " "))) (* " ")))
89 (? (: ":" (submatch (+ (~ " " "!" "@")))
90 (* (~ " ")) ;discard non-nick portion
92 (submatch (+ (~ " ")))
94 (? (submatch (+ any))))))
96 (define (string->msg string)
97 (let ((match (irregex-match msg-regex string)))
99 (irregex-match-substring match 1) ; Tags
100 (irregex-match-substring match 2) ; Source
101 (string-upcase (irregex-match-substring match 3)) ; command
102 (parse-message-args (irregex-match-substring match 4))))) ;args
104 (define (msg->string msg)
107 (let ((args (msg-args msg)))
108 (if args (conc " " (make-arg-string args)) ""))))
110 (define (make-arg-string args)
111 (let* ((revargs (reverse args))
112 (final-arg (car revargs))
113 (first-args (reverse (cdr revargs))))
114 (conc (string-join first-args " ")
117 (define (parse-message-args argstr)
119 (let ((idx (substring-index ":" argstr)))
122 (string-split (substring argstr 0 idx) " ")
123 (list (substring argstr (+ idx 1))))
124 (string-split argstr " ")))))
126 (define (msg-tags msg) (list-ref msg 0))
127 (define (msg-source msg) (list-ref msg 1))
128 (define (msg-command msg) (list-ref msg 2))
129 (define (msg-args msg) (list-ref msg 3))
131 (define (print-usage progname)
132 (let ((indent-str (make-string (string-length progname) #\space)))
134 progname " [-h/--help]\n"
135 progname " [-p/--port PORT] [--notls] [-c/--channnel CHANNEL]\n"
136 indent-str " proc-file host nick")))
139 (let ((progname (pathname-file (car (argv))))
142 (if (null? (command-line-arguments))
143 (print-usage progname)
144 (let loop ((args (command-line-arguments)))
145 (let ((this-arg (car args))
146 (rest-args (cdr args)))
147 (if (string-prefix? "-" this-arg)
149 ((or (equal? this-arg "-h")
150 (equal? this-arg "--help"))
151 (print-usage progname))
152 ((or (equal? this-arg "-p")
153 (equal? this-arg "--port"))
154 (set! irc-port (string->number (car rest-args)))
155 (loop (cdr rest-args)))
156 ((equal? this-arg "--notls")
159 ((or (equal? this-arg "-c")
160 (equal? this-arg "--channel"))
161 (set! bot-channel (car rest-args))
162 (loop (cdr rest-args)))
164 (print "Unknown argument '" this-arg "'")
165 (print-usage progname)))
167 ((procfile host nick)
168 (set! bot-proc-file procfile)
173 (print "One or more invalid arguments.")
174 (print-usage progname)))))))))