Clone this wiki locally
The web service is a built-in http server that can be used to remotely control or query the bot. It has the built-in ability to dispatch commands and retreive the plaintext response exactly like a IRC user would. Any plugin can easily add their own url routes to respond to requests.
- uses the ruby built-in WEBrick server implementation
- support for ssl, which is recommended if you expose the server to the internet
- easy to use interface for plugins similar to how irc command mapping work
- integrates in the security architecture of rbot, requesting HTTP authentication if necessary
- replaces the DRb remote interface with a more secure and portable solution
Web service configuration values can be modified using the irc interface.
||Boolean (false)||Whether the web service should be started automatically|
||Integer (7260)||Port on which the web service will listen|
||String (127.0.0.1)||Host the web service will bind on|
||Boolean (false)||Whether the web server should use SSL|
||String (~/.rbot/wskey.pem)||Private key file to use for SSL|
||String (~/.rbot/wscert.pem)||Certificate file to use for SSL|
||Boolean (true)||If you want to allow dispatching irc commands|
As you can see the web service does not start by default, you can start/stop it manually or set
<user> bot: webservice start <user> bot: webservice stop <user> bot: config set webservice.autostart true
If you want to use SSL for the web service you need to create a SSL certificate, to create a self signed certificate using openssl you can use:
openssl req -x509 -newkey rsa:2048 -keyout ~/.rbot/wskey.pem -out ~/.rbot/wscert.pem -days 2000 -nodes
You can also use a web server like nginx as a frontend server.
Requests are handled as the default bot user (without special permissions) you can send a HTTP authentication header to login as a bot user.
This allows you to execute a bot command just like a IRC user would, the configuration value
webservice.allow_dispatch can be used to deactivate this feature.
||string||IRC command (like in a private message to the bot)|
This will respond with a
text/plain response or if a
Accept: application/json header was sent in the request a
application/json json encoded response:
||array||Array with strings the bot said in response to the command|
% curl -d 'command=ping' http://127.0.0.1:7268/dispatch pong % curl -d 'command=help urban' http://127.0.0.1:7268/dispatch urban [word] [n]: give the [n]th definition of [word] from urbandictionary.com. urbanday: give the word-of-the-day at urban % curl -d 'command=urban matrix' http://127.0.0.1:7268/dispatch matrix (1/7): computer-generated dream world built to keep us under control in order to change a human being into a battery. NO ONE CAN BE TOLD WHAT THE MATRIX IS, YOU HAVE TO SEE IT FOR YOURSELF. k. % curl -d 'command=imdb the matrix' http://127.0.0.1:7268/dispatch Matrix (USA | Australia, 1999) : http://www.imdb.com/title/tt0133093/ Ratings: 8.7/10 (969,878 voters). Genre: Action/Sci-Fi. Plot: A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers
Plugins can register url "routes" they want to handle, right now the template syntax for this is not very featureful just supporting arguments
/method/:<argument> that match against the url path. Here is a minimal plugin that handles the url
class WSPlugin < Plugin include WebPlugin def hello(m, params) 'World!' end end plugin = WSPlugin.new plugin.web_map '/hello', :action => :hello
- Plugins need to include the
WebPluginmixin to use the
web_mapmethod to register handled urls.
helloaction method needs to be declared as a option parameter in
hellomethod returns a string that is then encoded as json and sent as a response.
Here is a another example that is using a url path parameter:
class WSPlugin < Plugin include WebPlugin def hello(m, params) name = params[:name] m.send_html('<h1>Hello, %s!</h1>' % [name]) end end plugin = WSPlugin.new plugin.web_map '/hello/:name', :action => :hello, :method => 'GET'
:<argument-name>is used as a optional argument and exposed in the params hash with
<argument-name>as a key.
:methodmapping option might be used to constrain a route to a HTTP method.
send_htmlhelper method takes a body and optionally a HTTP status code, that is then sent as a
text/htmlresponse. Other helpers include
send_response(body, [status], [content type]).
- You can also directly access the WEBrick request and response objects in
% curl http://127.0.0.1:7268/hello/world <h1>Hello, world!</h1>
Authentication / Security
If you want to restrict access to the route, you can do something like this: (This works similar to irc command mappings)
class WSPlugin < Plugin include WebPlugin def hello(m, params) 'World!' end end plugin = WSPlugin.new plugin.web_map '/hello', :action => :hello plugin.web_map '/restricted', :action => :hello, :auth_path => 'private' plugin.default_auth('private', false)
:auth_pathsets a specific auth path for this command.
default_authuses this auth path to restrict access to this action.
If you try to access the route:
% curl -i http://127.0.0.1:7268/restricted HTTP/1.1 401 Unauthorized Server: RBot Web Service (http://ruby-rbot.org/) Content-Type: text/plain Date: Mon, 12 Jan 2015 10:48:28 GMT Content-Length: 24 Connection: Keep-Alive Authentication Required!
You might use the default
owner user to access this, but its not recommended, instead you should create a new bot user and allow it to access the route (in irc):
<user> bot: user create hello swordfish <user> bot: permissions set +ws::private for hello
In this case "ws::private", ws is determined by the name of the plugin, this can be overwritten. Afterwards you can use basic HTTP auth to login and use the restricted command:
% curl -u hello:swordfish -i http://127.0.0.1:7268/restricted HTTP/1.1 200 OK Server: RBot Web Service (http://ruby-rbot.org/) Content-Type: application/json Date: Mon, 12 Jan 2015 11:01:25 GMT Content-Length: 8 Connection: Keep-Alive "World!"
Our original example used
default_auth to specify a single route that is restricted, but you can also do it the other way around, you specify all routes as restricted and allow the ones you want to make unrestricted:
class WSPlugin < Plugin include WebPlugin def hello(m, params) 'World!' end end plugin = WSPlugin.new plugin.web_map '/hello', :action => :hello, :auth_path => 'public' plugin.web_map '/restricted', :action => :hello plugin.default_auth('*', false) plugin.default_auth('public', true)