(local privmsg (require :privmsg))
(local handlers (require :handlers))
(fn ok-term? [term] (and (: term :match "^[-%w]+$") term))
(fn term-name [term] (.. "terms/" term))
(fn handlers.set [_ _ args]
(let [(term data) (: args :match "^(%S+) (.*)")
f (file.open (term-name term) :w)]
(if f
(do (doto f
(: :write data)
(: :close))
(.. "OK, " term " is now " data))
"Could not write term for some reason.")))
(fn handlers.get [_ _ term]
(.. term " is " (if (file.exists (term-name term))
(let [file (file.open (term-name term))
value (file.read)]
(file.close)
value)
"not something I know about.")))
(fn send [socket command use-separator? args]
(var str command)
(when (> (# args) 0)
(let [sep (if use-separator?
" :"
(> (# args) 1)
" " "")]
(set str (.. str " " (table.concat args " " 1 (- (# args) 1))
sep (. args (# args))))))
(when _G.debug? (print ">" str))
(: socket :send (.. str "\r\n")))
(fn push [conn command use-separator? args]
(tset conn.queue conn.bottom [command use-separator? args])
(set conn.bottom (+ conn.bottom 1)))
(fn flush [conn]
(when (not (= conn.top conn.bottom))
(let [[command separator? args] (. conn.queue conn.top)]
(tset conn.queue conn.top nil)
(set conn.top (+ conn.top 1))
(when (= conn.top conn.bottom)
(set conn.top 1)
(set conn.bottom 1))
(send conn.socket command separator? args))))
(fn tokenize [line]
(let [(_ e1 prefix) (: line :find "^:(%S+)")
(_ e2 command) (: line :find "(%S+)" (if e1 (+ e1 1) 1))
(_ _ rest) (: line :find "%s+(.*)" (if e2 (+ e2 1) 1))]
(values prefix command rest)))
(var commands nil)
(fn receive [conn socket data]
(flush conn)
(each [line (: data :gmatch "[^\r\n]+")]
(when line
(when _G.debug? (print "<" line))
(let [(prefix command rest) (tokenize line)
command (and command (: command :lower))
handler (. commands command)]
(when handler
(handler prefix rest))))))
(fn connect [irc host port nick channels]
(let [conn {:queue [] :top 1 :bottom 1
:socket (net.createConnection) :nick nick}
t (tmr.create)]
(: conn.socket :connect (or port 6667) host)
(: conn.socket :on :receive (partial receive conn))
(: conn.socket :on :connection (fn []
(irc.nick conn nick)
(irc.user conn "pengbot" "0" "*"
"A bot written in fennel.")
(print "Connected!")
(irc.start irc conn channels)))
(: conn.socket :on :disconnection (fn [x err]
(print "oh no" x err)
(: t :stop)))
(: t :alarm 200 tmr.ALARM_AUTO (partial flush conn))
conn))
(let [irc {:connect connect
:start (fn [irc conn channels]
(global connection conn)
(set commands {:ping (fn [prefix rest] (irc.pong conn rest))
:privmsg (partial irc.privmsg* conn)
:376 (fn [prefix rest]
(each [_ channel (ipairs channels)]
(let [channel (if (: channel :find "^#")
channel (.. "#" channel))]
(irc.join conn channel))))}))
:flush flush}]
(each [name separator? (pairs {:nick false :user true :join false :kick true
:privmsg true :ping false :pong false :part true})]
(tset irc name
(fn [conn ...]
(push conn (: name :upper) separator? [...]))))
(fn irc.privmsg* [conn prefix rest]
(let [chan (: rest :match "(%S+)")
msg (: rest :match ":(.*)")
their-nick (: prefix :match "(%S+)!")
(cmd args) (if (string.find msg "^,")
(: msg :match "^,(%S+) ?(.*)")
(: msg :match (string.format "^%s: (%%S+) ?(.*)"
conn.nick)))
chan (if (: chan :find "^#") chan their-nick)
handler (. handlers cmd)]
(when handler
(let [(ok response) (pcall handler conn chan args)]
(if (and ok (= (type response) "string"))
(irc.privmsg conn chan response)
(not ok)
(print "Error in handler" response))))))
irc)