summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias H <apoc@sixserv.org>2014-02-21 17:51:28 +0100
committerMatthias H <apoc@sixserv.org>2014-02-21 17:51:28 +0100
commitba5aaf073e5467b0b71adede32051f9b38c50a19 (patch)
treedef2d2ac2ffa0e6027188e6e01bf48d738926abb
parent8f23ec47bf539f8831719745e80c1bdaef966f6a (diff)
[webservice] control bot through http interface
-rw-r--r--lib/rbot/botuser.rb2
-rw-r--r--lib/rbot/core/webservice.rb173
-rw-r--r--lib/rbot/ircbot.rb6
-rw-r--r--lib/rbot/messagemapper.rb6
4 files changed, 185 insertions, 2 deletions
diff --git a/lib/rbot/botuser.rb b/lib/rbot/botuser.rb
index 110c078f..ab9a8de3 100644
--- a/lib/rbot/botuser.rb
+++ b/lib/rbot/botuser.rb
@@ -872,7 +872,7 @@ class Bot
if user.class <= BotUser
botuser = user
else
- botuser = irc_to_botuser(user)
+ botuser = user.botuser
end
cmd = cmdtxt.to_irc_auth_command
diff --git a/lib/rbot/core/webservice.rb b/lib/rbot/core/webservice.rb
new file mode 100644
index 00000000..222561de
--- /dev/null
+++ b/lib/rbot/core/webservice.rb
@@ -0,0 +1,173 @@
+#-- vim:sw=2:et
+#++
+#
+# :title: Web service for bot
+#
+# Author:: Matthias Hecker (apoc@geekosphere.org)
+#
+# HTTP(S)/json based web service for remote controlling the bot,
+# similar to remote but much more portable.
+#
+# For more info/documentation:
+# https://github.com/4poc/rbot/wiki/Web-Service
+#
+
+require 'webrick'
+require 'webrick/https'
+require 'openssl'
+require 'cgi'
+
+class ::WebServiceUser < Irc::User
+ def initialize(str, botuser, opts={})
+ super(str, opts)
+ @botuser = botuser
+ @response = []
+ end
+ attr_reader :botuser
+ attr_accessor :response
+end
+
+class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
+ def initialize(server, bot)
+ super server
+ @bot = bot
+ end
+
+ def do_POST(req, res)
+ # NOTE: still wip.
+ uri = req.path_info
+ post = CGI::parse(req.body)
+ ip = req.peeraddr[3]
+
+ command = uri.gsub('/', ' ').strip
+
+ username = post['username'].first
+ password = post['password'].first
+
+ botuser = @bot.auth.get_botuser(username)
+ netmask = '%s!%s@%s' % [botuser.username, botuser.username, ip]
+
+ if not botuser or botuser.password != password
+ raise 'Permission Denied'
+ end
+
+ ws_user = WebServiceUser.new(netmask, botuser)
+ message = Irc::PrivMessage.new(@bot, nil, ws_user, @bot.myself, command)
+
+ @bot.plugins.irc_delegate('privmsg', message)
+
+ res.status = 200
+ res['Content-Type'] = 'text/plain'
+ res.body = ws_user.response.join("\n") + "\n"
+ end
+end
+
+class WebServiceModule < CoreBotModule
+
+ Config.register Config::BooleanValue.new('webservice.autostart',
+ :default => false,
+ :requires_rescan => true,
+ :desc => 'Whether the web service should be started automatically')
+
+ Config.register Config::IntegerValue.new('webservice.port',
+ :default => 7260, # that's 'rbot'
+ :requires_rescan => true,
+ :desc => 'Port on which the web service will listen')
+
+ Config.register Config::StringValue.new('webservice.host',
+ :default => '127.0.0.1',
+ :requires_rescan => true,
+ :desc => 'Host the web service will bind on')
+
+ Config.register Config::BooleanValue.new('webservice.ssl',
+ :default => false,
+ :requires_rescan => true,
+ :desc => 'Whether the web server should use SSL (recommended!)')
+
+ Config.register Config::StringValue.new('webservice.ssl_key',
+ :default => '~/.rbot/wskey.pem',
+ :requires_rescan => true,
+ :desc => 'Private key file to use for SSL')
+
+ Config.register Config::StringValue.new('webservice.ssl_cert',
+ :default => '~/.rbot/wscert.pem',
+ :requires_rescan => true,
+ :desc => 'Certificate file to use for SSL')
+
+ def initialize
+ super
+ @port = @bot.config['webservice.port']
+ @host = @bot.config['webservice.host']
+ @server = nil
+ begin
+ start_service if @bot.config['webservice.autostart']
+ rescue => e
+ error "couldn't start web service provider: #{e.inspect}"
+ end
+ end
+
+ def start_service
+ raise "Remote service provider already running" if @server
+ opts = {:BindAddress => @host, :Port => @port}
+ if @bot.config['webservice.ssl']
+ opts.merge! :SSLEnable => true
+ cert = File.expand_path @bot.config['webservice.ssl_cert']
+ key = File.expand_path @bot.config['webservice.ssl_key']
+ if File.exists? cert and File.exists? key
+ debug 'using ssl certificate files'
+ opts.merge!({
+ :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(cert)),
+ :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(key))
+ })
+ else
+ debug 'using on-the-fly generated ssl certs'
+ opts.merge! :SSLCertName => [ %w[CN localhost] ]
+ # the problem with this is that it will always use the same
+ # serial number which makes this feature pretty much useless.
+ end
+ end
+ @server = WEBrick::HTTPServer.new(opts)
+ debug 'webservice started: ' + opts.inspect
+ @server.mount('/dispatch', DispatchServlet, @bot)
+ Thread.new { @server.start }
+ end
+
+ def stop_service
+ @server.shutdown if @server
+ @server = nil
+ end
+
+ def cleanup
+ stop_service
+ super
+ end
+
+ def handle_start(m, params)
+ s = ''
+ if @server
+ s << 'web service already running'
+ else
+ begin
+ start_service
+ s << 'web service started'
+ rescue
+ s << 'unable to start web service, error: ' + $!.to_s
+ end
+ end
+ m.reply s
+ end
+
+end
+
+webservice = WebServiceModule.new
+
+webservice.map 'webservice start',
+ :action => 'handle_start',
+ :auth_path => ':manage:'
+
+webservice.map 'webservice stop',
+ :action => 'handle_stop',
+ :auth_path => ':manage:'
+
+webservice.default_auth('*', false)
+
diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb
index 0a8b1aa2..a342e8c1 100644
--- a/lib/rbot/ircbot.rb
+++ b/lib/rbot/ircbot.rb
@@ -1128,6 +1128,12 @@ class Bot
where = ds[:dest]
filtered = ds[:text]
+ if defined? WebServiceUser and where.instance_of? WebServiceUser
+ debug 'sendmsg to web service!'
+ where.response << filtered
+ return
+ end
+
# For starters, set up appropriate queue channels and rings
mchan = opts[:queue_channel]
mring = opts[:queue_ring]
diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb
index d85f0b14..e154e395 100644
--- a/lib/rbot/messagemapper.rb
+++ b/lib/rbot/messagemapper.rb
@@ -258,7 +258,11 @@ class Bot
debug "checking auth for #{auth}"
if m.bot.auth.allow?(auth, m.source, m.replyto)
debug "template match found and auth'd: #{action.inspect} #{options.inspect}"
- if !m.in_thread && (tmpl.options[:thread] || tmpl.options[:threaded])
+ if !m.in_thread and (tmpl.options[:thread] or tmpl.options[:threaded]) and
+ (defined? WebServiceUser and not m.source.instance_of? WebServiceUser)
+ # Web service: requests are handled threaded anyway and we want to
+ # wait for the responses.
+
# since the message action is in a separate thread, the message may be
# delegated to unreplied() before the thread has a chance to actually
# mark it as replied. since threading is used mostly for commands that