irc-bot/contrib/modbot.lua

319 lines
5.7 KiB
Lua
Raw Normal View History

2013-01-30 22:30:22 -07:00
#! /usr/bin/lua
channel = "testchan"
--
-- A bot framework with a bunch of nifty features you can
-- call from your privmsg handler.
--
-- You should override these in your handler. Don't change
-- them here, though, or you'll have to keep changing them
-- every time you install a new release.
bot = {}
bot.debug = false
bot.nick = channel .. "-moderator"
bot.user = "moderator"
bot.desc = "Channel Moderation Fairy"
bot.channels = {"#" .. channel, "#mod-" .. channel}
bot.triggers = {}
------------------------------------------------------------
-- Some convenience functions
--
function shesc(s)
return "'" .. s:gsub("'", "'\\''") .. "'"
end
function syscmd(argv)
local args = {}
local cmdline
local proc
for _,a in ipairs(argv) do
table.insert(args, shesc(a))
end
cmdline = table.concat(args, " ")
proc = io.popen(cmdline, "r")
return function ()
return proc:read()
end
end
function system(argv)
local ret = {}
for line in syscmd(argv) do
table.insert(ret, line)
end
return ret
end
function string:startswith(needle)
local len = needle:len()
return self:sub(1, len) == needle
end
function string:endswith(needle)
local len = needle:len()
return self:sub(-len) == needle
end
------------------------------------------------------------
-- Bot object
--
bot.prefix = os.getenv("prefix")
bot.forum = os.getenv("forum")
bot.sender = os.getenv("sender")
bot.command = os.getenv("command")
bot.text = os.getenv("text")
bot.args = arg
--
-- Log to stderr
--
function bot:log(text)
io.stderr:write(tostring(text) .. "\n")
end
--
-- Log what we are working with
--
function bot:debug_input(text)
self:log(("< %-8s %8s/%-8s :%s"):format(
self.command,
self.sender or "-",
self.forum or "-",
self.text or ""))
end
--
-- Send a raw IRC command
--
function bot:raw(text)
if self.debug then
self:log(" > " .. text)
end
print(text)
end
--
-- Send a message to the forum, falling back to sender if we're
-- spamming the channel.
--
function bot:msg(text)
self.msgs_sent = self.msgs_sent + 1
if ((self.msgs_sent == 5) and (self.forum ~= self.sender)) then
self:raw("PRIVMSG " .. self.forum .. " :Sending the rest in private")
self.msg_recip = self.sender
end
self:raw("PRIVMSG " .. self.msg_recip .. " :" .. text)
end
bot.msgs_sent = 0
bot.msg_recip = bot.forum
--
-- Emote, like "* botname explodes"
--
function bot:emote(text)
self:msg("\001ACTION " .. text .. "\001")
end
--
-- Run something in the shell
--
function bot:syscmd(argv)
for line in syscmd(argv) do
if line:sub(1, 1) == ':' then
self:emote(line:sub(2))
else
self:msg(line)
end
end
end
-- Process self.triggers
function bot:do_triggers()
for pat,func in pairs(self.triggers) do
local a1, a2, a3, a4, a5 = self.text:match(pat)
if a1 and func then
if type(func) == "string" then
self:msg(func)
else
func(self, a1, a2, a3, a4, a5, a6)
end
return true
end
end
end
-- Use introspection to dispatch a command
function bot:run()
local func = self["handle_" .. self.command:lower()]
if (self.debug) then
self:debug_input()
end
if (func) then
func(self)
else
self:handle_default()
end
end
-- Log in to IRC
function bot:handle__init_()
self:raw("NICK " .. self.nick)
self:raw(("USER %s %s %s :%s"):format(self.user, self.user, self.user, self.desc))
end
-- Join channels
function bot:handle_001()
self:raw("PRIVMSG nickserv :identify modbot4pescado.fleas")
end
-- Deal with nickname collision
function bot:handle_433()
self:raw("NICK " .. self.nick .. (os.time() % 500))
end
-- Override this to handle messages
function bot:handle_privmsg()
self:do_triggers()
end
-- Override this to handle any undefined command
function bot:handle_default()
end
---------------------------------
--
-- Moderation
--
--
require('lfs')
function queue()
ret = {}
for file in lfs.dir("queue") do
if file:sub(1, 1) ~= "." then
table.insert(ret, file)
end
end
return ret
end
function push(text)
now = os.time()
f = io.open(("queue/%d"):format(now), "w")
f:write(text)
f:close()
end
function pop(q, peek)
if not q then
q = queue()
end
if #q == 0 then
return
end
table.sort(q)
fn = ("queue/%s"):format(q[1])
f = io.open(fn)
ret = f:read()
f:close()
if peek == nil then
os.remove(fn)
table.remove(q, 1)
end
return ret
end
function peek(q)
return pop(q, true)
end
function bot:handle_notice()
if (self.sender == 'NickServ') and self.text:startswith("You are successfully identified") then
for _,chan in ipairs(self.channels) do
self:raw("JOIN " .. chan)
end
end
end
function bot:handle_privmsg()
if self.forum:sub(1, 1) == "#" then
self:handle_public()
else
self:handle_private()
end
end
function bot:handle_public()
if self.forum:startswith("#mod-") then
q = queue()
if self.text:startswith("!") then
if self.text:startswith("!n") then
question = pop(q)
if not question then
return self:msg("[Queue empty]")
else
self:raw("PRIVMSG " .. self.channels[1] .. " :" .. question)
end
elseif self.text:startswith("!d") then
pop(q)
end
question = pop(q, true)
if question then
self:msg(("[1/%d] %s"):format(#q, question))
else
self:msg("[End of Queue]")
end
end
end
end
function bot:handle_private()
ok = false
for addr in io.lines("authorized.txt") do
if self.text:startswith(addr) then
ok = true
end
end
if not ok then
return
end
q = queue()
question = ("<%s> %s"):format(self.prefix, self.text)
push(question)
self:msg("Your question has been enqueued. There are approximately " .. #q .. " questions before yours.")
if (#q == 0) then
self:raw("PRIVMSG " .. self.channels[2] .. " :[1/1] " .. question)
end
end
bot.debug = true
bot:run()