4 # :title: Remote service provider for rbot
6 # Author:: Giuseppe Bilotta (giuseppe.bilotta@gmail.com)
7 # Copyright:: Copyright (c) 2006 Giuseppe Bilotta
10 # From an idea by halorgium <rbot@spork.in>.
12 # TODO client ID and auth
13 # TODO Irc::Plugins::RemotePlugin module to be included by plugins that want to
14 # provide a remote interface. Such module would define a remote_map() method
15 # that would register the plugin to received mapped commands from remote clients.
16 # FIXME how should be handle cleanups/rescans? Probably just clear() the
17 # RemoteDispatcher template list. Provide a cleanup() method for
18 # RemoteDispatcher and think about this.
24 # A RemoteCommand is similar to a BaiscUserMessage
30 # when the message was received
33 # remote client that originated the message
36 # contents of the message
37 attr_accessor :message
39 def initialize(bot, source, message)
46 # The target of a RemoteMessage
51 # Remote messages are always 'private'
57 class RemoteDispatcher < MessageMapper
63 # We redefine the handle() method from MessageMapper, taking into account
64 # that @parent is a bot, and that we don't handle fallbacks
66 # TODO this same kind of mechanism could actually be used in MessageMapper
67 # itself to be able to handle the case of multiple plugins having the same
71 return false if @templates.empty?
73 @templates.each do |tmpl|
74 botmodule = @parent.plugins[tmpl.botmodule]
75 options, failure = tmpl.recognize(m)
77 failures << [tmpl, failure]
79 action = tmpl.options[:action]
80 unless botmodule.respond_to?(action)
81 failures << [tmpl, "#{botmodule} does not respond to action #{action}"]
84 auth = tmpl.options[:full_auth_path]
85 debug "checking auth for #{auth}"
86 if m.bot.auth.allow?(auth, m.source, m.replyto)
87 debug "template match found and auth'd: #{action.inspect} #{options.inspect}"
88 @parent.send(action, m, options)
91 debug "auth failed for #{auth}"
92 # if it's just an auth failure but otherwise the match is good,
93 # don't try any more handlers
98 debug "#{f.inspect} => #{r}"
100 debug "no handler found"
108 # The Irc::IrcBot::RemoteObject class represents and object that will take care
109 # of interfacing with remote clients
113 # We don't want this object to be copied clientside, so we make it undumpable
116 # Initialization is simple
119 @dispatcher = RemoteDispatcher.new(@bot)
122 # The delegate method. This is the main method used by remote clients to send
123 # commands to the bot. Most of the time, the method will be called with only
124 # two parameters (authorization code and a String), but we allow more parameters
125 # for future expansions
127 def delegate(auth, *pars)
128 warn "Ignoring extra parameters" if pars.length > 1
130 # TODO implement auth <-> client conversion
131 # We need at least a RemoteBotUser class derived from Irc::Auth::BotUser
132 # and a way to associate the auth info to the appropriate RemoteBotUser class
134 debug "Trying to dispatch command #{cmd.inspect} authorized by #{auth.inspect}"
135 m = RemoteMessage.new(@bot, client, cmd)
136 @dispatcher.handle(m)
140 # The bot also manages a single (for the moment) remote object. This method
141 # makes it accessible to the outside world, creating it if necessary.
144 if defined? @remote_object
147 @remote_object = RemoteObject.new(self)
155 class RemoteModule < CoreBotModule
157 BotConfig.register BotConfigIntegerValue.new('remote.port',
158 :default => 7268, # that's 'rbot'
159 :on_change => Proc.new { |bot, v|
164 :requires_restart => true,
165 :desc => "Port on which the remote interface will be presented")
167 BotConfig.register BotConfigStringValue.new('remote.host',
169 :on_change => Proc.new { |bot, v|
174 :requires_restart => true,
175 :desc => "Port on which the remote interface will be presented")
179 @port = @bot.config['remote.port']
180 @host = @bot.config['remote.host']
186 raise "Remote service provider already running" if @drb
187 @drb = DRb.start_service("druby://#{@host}:#{@port}", @bot.remote_object)
191 @drb.stop_service if @drb
200 def handle_start(m, params)
202 rep = "remote service provider already running"
203 rep << " on port #{@port}" if m.private?
207 rep = "remote service provider started"
208 rep << " on port #{@port}" if m.private?
210 rep = "couldn't start remote service provider"
218 remote = RemoteModule.new
220 remote.map "remote start",
221 :action => 'handle_start',
222 :auth_path => ':manage:'
224 remote.map "remote stop",
225 :action => 'handle_stop',
226 :auth_path => ':manage:'
228 remote.default_auth('*', false)