golbot
golbot copied to clipboard
A single binary Lua scriptable chat bot with REST API support written in Go
golbot - A Lua scriptable chat bot
golbot is a Lua scriptable chat bot written in Go.
Supported procotols
- IRC
- Slack
- Hipchat
- RocketChat
Install
go get github.com/yuin/golbot
Getting started
golbot init PROTOCOL # golbot.lua will be generated
golbot run
Scripting with Lua
Here is a default config script for IRC :
local golbot = require("golbot") -- 1
local json = require("json")
local charset = require("charset")
-- local re = require("re")
-- local requests = require("requests")
-- local sh = require("sh")
-- local fs = require("fs")
function main()
mynick = "golbot"
myname = "golbot"
msglog = golbot.newlogger({"seelog", type="adaptive", mininterval="200000000", maxinterval="1000000000", critmsgcount="5",
{"formats",
{"format", id="main", format="%Date(2006-01-02 15:04:05) %Msg"},
},
{"outputs", formatid="main",
{"filter", levels="trace,debug,info,warn,error,critical",
{"console"}
},
}
})
local bot = golbot.newbot("IRC", { -- 2
nickname = mynick,
username = myname,
conn = "localhost:6667,#test",
useTLS = false,
password = "password",
http = "0.0.0.0:6669",
log = {"seelog", type="adaptive", mininterval="200000000", maxinterval="1000000000", critmsgcount="5",
{"formats",
{"format", id="main", format="%Date(2006-01-02 15:04:05) [%Level] %Msg"},
},
{"outputs", formatid="main",
{"filter", levels="trace,debug,info,warn,error,critical",
{"console"}
},
}
}
})
bot:respond([[\s*(\d+)\s*\+\s*(\d+)\s*]], function(m, e) -- 3
bot:say(e.target, tostring(tonumber(m[2]) + tonumber(m[3])))
end)
bot:on("PRIVMSG", function(e) -- 4
local ch = e.arguments[1]
local nick = e.nick
local user = e.user
local source = e.source
local msg = e:message()
if nick == mynick then
return
end
msglog:printf("%s\t%s\t%s", ch, source, msg)
bot.raw:privmsg(ch, msg) -- 5
goworker({channel=ch, message=msg, nick=nick}) -- 6
end)
bot:serve(function(msg) -- 7
if msg.type == "say" then
bot:say(msg.channel, msg.message)
respond(msg, true) -- 8
end
end)
end
function worker(msg) -- 9
notifymain({type="say", channel=msg.channel, message="accepted"}) -- 10
end
function http(r) -- 11
if r.method == "POST" and r.URL.path == "/say" then
local msg = json.decode(r:readbody())
local ok, success = requestmain({type="say", channel=msg.channel, message=msg.message) -- 12
if ok and success then
return 200,
{
{"Content-Type", "application/json; charset=utf-8"}
},
json.encode({result="ok"})
else
return 406,
{
{"Content-Type", "application/json; charset=utf-8"}
},
json.encode({result="error"})
end
end
return 400,
{
{"Content-Type", "text/plain; charset=utf-8"}
},
"NOT FOUND"
end
-
- requires golbot library.
-
- creates new bot.
#1: chat type. currently supports"IRC","Slack","Hipchat"and"Rocket"#2: options(including protocol specific) as a table- Common options are:
log:function: function to log system messages(function(msg:string) end)table: seelog XML configuration as a lua table to log system messages
http: Address with port for binding HTTP REST API server
nickname,username,conn,userTLSandpasswordare IRC specific options
- Common options are:
-
- adds a callback that will be called when bot receives a message.
respondwill be called only when the message contains a mention to the bot.
#1: regular expression(this value will be evaluated by Go's regexp package)#2: callback functionm(table): captured groups as a list of strings.e(object): message event object:target(string)from(string)message(string)raw(object): underlying procotol specific object
- adds a callback that will be called when bot receives a message.
-
- adds a callback for procotol specific events.
#1: event name#2: callback functione(object): procotol specific event object
-
- calls underlying procotol specific client methods.
-
- creates a new goroutine and sends a message to it.
-
- starts main goroutine.
#1: callback function that will be called when messages are sent by worker goroutines. The callback function that will be called when messages are sent by main gorougine.
-
- responds to the message from other goroutines.
-
- a function that will be executed in worker goroutines.
-
- sends a message to the main goroutine.
-
- a function that will be executed when http requests are arrived.
-
- sends a message to the main goroutine and receives a result from the main goroutine.
IRC
golbot uses go-ircevent as an IRC client, GopherLua as a Lua script runtime, and gopher-luar as a data converter between Go and Lua.
golbot init ircgenerates a default lua config for IRC bots.golbot.newbotcreates new*irc.Connection(ingo-ircevent) object wrapped by gopher-luar, sobot.rawhas same methods as*irc.Connection.- Protocol specific event object has same method as
*irc.Event - Protocol specific event names are same as first argument of
*irc.Connection#AddCallback. - Protocol specific options for
golbot.newbotare:conn(string):"host:port,#channel,#channel..."useTLS(bool)password(string)
- There is no official "mention" functionallity in IRC.
@nick,:nickand\nickare treated as a mention inrespond.
Slack
golbot uses slack as a Slack client.
golbot init slackgenerates a default lua config for Slack bots.golbot.newbotcreates new*slack.RTM(inslack) object wrapped by gopher-luar, sobot.rawhas same methods as*slack.RTM.- Protocol specific event object has same method as
*slack.*Event. Events are listed near line 349 of websocket_managed_conn.go . - Protocol specific event names are same as message type of Slack API
- Protocol specific options for
golbot.newbotare:token(string): Bot token
Hipchat
golbot uses hipchat as a Hipchat XMPP client.
golbot init hipchatgenerates a default lua config for Hipchat bots.golbot.newbotcreates new*hipchat.Client(inhipchat) object wrapped by gopher-luar, sobot.rawhas same methods as*hipchat.Client.- Protocol specific event object has same method as
*hipchat.Message. - Protocol specific event names are
"message"
- Protocol specific options for
golbot.newbotare:user(string): Hipchat XMPP user name. You can find it on the Hipchat account page(https://yourdomain.hipchat.com/account/xmpp)password(string): passwordhost(string): hostname like"chat.hipchat.com".conf(string): conf hostname like"conf.hipchat.com".auth_type(string): authentication type. ("plain"or"oauth")room_jids(list of string): XMPP JIDs like{"[email protected]", "[email protected]"}
As described above golbot can also act as an HTTP(S) server, so you can use golbot as your own integration for Hipchat.
local golbot = require("golbot")
local json = require("json")
local requests = require("requests")
function main()
golbot.newbot("Null", { http = "0.0.0.0:6669" }):serve(function() end)
end
local headers = {
{"Content-Type", "application/json; charset=utf-8"}
}
function http(r)
if r.method == "POST" and r.URL.path == "/webhook" then
local data = assert(json.decode(r:readbody()))
local message = data.item.message.message
local user = data.item.message.from.name
local room = data.item.room.name
local ret = {
message = "hello! from webhook",
message_format = "html"
}
return 200, headers, json.encode(ret)
end
return 400, headers, json.encode({result="not found"})
end
RocketChat
golbot uses gorocket as a RocketChat client.
golbot init rocketgenerates a default lua config for RocketChat bots.golbot.newbotcreates new*realtime.Client(ingorocket) object wrapped by gopher-luar, sobot.rawhas same methods as*realtime.Client.- Protocol specific event object has same method as
*apl.Message. - Protocol specific options for
golbot.newbotare:url(string): RocketChat urlname(string): User nameemail(string): User emailpassword(string): User passwordchannnels(string): Comma-separated channels to join such as "general,releasejobs" .
Logging
golbot is integrated with seelog . golbot.newlog(tbl) creates a new logger that has a printf method.
log = golbot.newlog(conf)
log:printf("[info] msg")
First word surrounded by [] is a log level in seelog. Rest are actual messages.
Working with non-UTF8 servers
charset module provides character set conversion functions.
charset.encode(string, charset): convertsstringfrom utf-8 tocharset.charset.decode(string, charset): convertsstringfromcharsetto utf-8 .
Examples:
bot:say("#ch", charset.encode("ããã«ã¡ã", "iso-2022-jp"))
bot:respond(".*", function(m, e)
bot.log:printf("%s", charset.decode(e.message, "iso-2022-jp"))
end)
Groutine model
golbot consists of multiple goroutines.
- main goroutine : connects and communicates with chat servers.
- worker goroutines : typically execute function may takes a long time.
- http goroutines : handle REST API requests.
Consider, for example, the case of deploying web applications when receives a special message.
bot:respond("do_deploy", function(m, e)
do_deploy()
end)
do_deploy() may takes a minute, main goroutine is locked all the time. For avoiding this, do_deploy should be executed in worker goroutines via messaging(internally, golbot uses Go channels).
function main()
-- blah blah
bot:respond("do_deploy", function(m, e)
goworker({ch=e.target, message="do_deploy"})
bot:say(e.target, "Your deploy request is accepted. Please wait a minute.")
end)
bot:serve(function(msg)
bot:say(msg.ch, msg.message)
end)
end
function worker(msg)
if msg.message == "do_deploy" then
do_deploy()
notifymain({ch=msg.ch, message="do_deploy successfully completed!"})
end
end
golbot provides functions that simplify communications between goroutines through channels.
- goworker(msg:table) : creates a new goroutine and sends the
msgto it. - notifymain(msg:table) : sends the
msgto the main goroutine. - requestmain(msg:table) : sends the
msgto the main goroutine and receives a result from the main goroutine. - respond(requestmsg:table, result:any) : sends the
resultto the requestor.
Create REST API
If the http global function exists in the golbot.lua, REST API feature will be enabled.
function http(r)
if r.method == "POST" and r.URL.path == "/say" then
local msg = json.decode(r:readbody())
local ok, success = requestmain({type="say", channel=msg.channel, message=msg.message})
if ok and success then
return 200,
{
{"Content-Type", "application/json; charset=utf-8"}
},
json.encode({result="ok"})
else
return 406,
{
{"Content-Type", "application/json; charset=utf-8"}
},
json.encode({result="error"})
end
end
return 400,
{
{"Content-Type", "text/plain; charset=utf-8"}
},
"NOT FOUND"
end
http function receives net/http#Request object wrapped by gopher-luar .
http function must return 3 objects:
- HTTP status:number : HTTP status code.
- headers:table : a table contains response headers.
- contents:string : response body.
http function will be executed in its own thread. You can use notify* and request* functions for communicating with other goroutines.
TLS support
An https global function and an https option for golbot.newbot enable TLS support.
local bot = golbot.newbot("IRC", { -- 2
-- blah blah...
https = {
addr = "0.0.0.0:7000",
cert = "server.crt",
key = "server.key"
},
})
function https(r)
-- same as `http`
end
Make HTTP requests
requests module provides some utilities for making HTTP requests.
requests.request(opt:table)#1: optionsmethod(string): HTTP method, such as"GET". This defaults to"GET".url(string): URLdata(string): Request bodyparam: List of parameters such as{name1, value1, name2, value2}- If
methodisGET, this value will be sent as query strings. - Otherwise, this value will be sent as a form-urlencoded value and
Content-Type: application/x-www-form-urlencodedheader will also be sent.
- If
headers: List of headers such as{name1, value1, name2, value2}
- Retuens
body(string)andresponse(net/http#Response wrapped by gopher-luar)if succeeded ornilanderror(string).
requests.json(opt:table)#1: options. Almost same asrequests.request. The following additional options are available.json(table): This value will be encoded into a json string and sent as a request body.
- Retuens
obj(table)andresponse(net/http#Response wrapped by gopher-luar)if succeeded ornilanderror(string). Content-type: application/jsonheader will automatically be added to the request headers.
body, response = requests.request({
method="POST",
url="http://example.com/",
headers={"Content-Type", "application/json"},
data=json.encode(data) })
-- same as above
obj, response = requests.json({
method="POST",
url="http://example.com/",
json=data })
Execute scheduled jobs
A crons option for golbot.newbot enables cron like scheduled jobs.
function main()
golbot.newbot("Null", {
http = "0.0.0.0:6669" ,
crons = {
{ "0 * * * * * ", "job1"}
}
}):serve(function() end)
end
function job1()
print "hello!"
end
golbot uses cron to implement this functionality, first element of a job can be 'CRON Expression Format' in cron .
Bundled Lua libraries
Author
Yusuke Inuzuka