golbot is a Lua scriptable chat bot written in Go.
- IRC
- Slack
- Hipchat
- RocketChat
go get github.com/yuin/golbot
golbot init PROTOCOL # golbot.lua will be generated
golbot run
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
,userTLS
andpassword
are IRC specific options
- Common options are:
-
- adds a callback that will be called when bot receives a message.
respond
will 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.
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 irc
generates a default lua config for IRC bots.golbot.newbot
creates new*irc.Connection
(ingo-ircevent
) object wrapped by gopher-luar, sobot.raw
has 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.newbot
are:conn(string)
:"host:port,#channel,#channel..."
useTLS(bool)
password(string)
- There is no official "mention" functionallity in IRC.
@nick
,:nick
and\nick
are treated as a mention inrespond
.
golbot uses slack as a Slack client.
golbot init slack
generates a default lua config for Slack bots.golbot.newbot
creates new*slack.RTM
(inslack
) object wrapped by gopher-luar, sobot.raw
has 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.newbot
are:token(string)
: Bot token
golbot uses hipchat as a Hipchat XMPP client.
golbot init hipchat
generates a default lua config for Hipchat bots.golbot.newbot
creates new*hipchat.Client
(inhipchat
) object wrapped by gopher-luar, sobot.raw
has 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.newbot
are: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{"111111_xxxxx@conf.hipchat.com", "111111_yyyy@conf.hipchat.com"}
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
golbot uses gorocket as a RocketChat client.
golbot init rocket
generates a default lua config for RocketChat bots.golbot.newbot
creates new*realtime.Client
(ingorocket
) object wrapped by gopher-luar, sobot.raw
has same methods as*realtime.Client
.- Protocol specific event object has same method as
*apl.Message
. - Protocol specific options for
golbot.newbot
are:url(string)
: RocketChat urlname(string)
: User nameemail(string)
: User emailpassword(string)
: User passwordchannnels(string)
: Comma-separated channels to join such as "general,releasejobs" .
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.
charset
module provides character set conversion functions.
charset.encode(string, charset)
: convertsstring
from utf-8 tocharset
.charset.decode(string, charset)
: convertsstring
fromcharset
to 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)
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
msg
to it. - notifymain(msg:table) : sends the
msg
to the main goroutine. - requestmain(msg:table) : sends the
msg
to the main goroutine and receives a result from the main goroutine. - respond(requestmsg:table, result:any) : sends the
result
to the requestor.
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.
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
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
method
isGET
, 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-urlencoded
header 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 ornil
anderror(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 ornil
anderror(string)
. Content-type: application/json
header 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 })
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
.
Yusuke Inuzuka