Class: Safubot::Bot
- Inherits:
-
Object
- Object
- Safubot::Bot
- Includes:
- Evented
- Defined in:
- lib/safubot/bot.rb,
lib/safubot/test_helper.rb
Overview
The main event-processing class. You are encouraged to inherit from this class when building your own bot, but delegation is also entirely feasible.
Instance Attribute Summary (collapse)
-
- (Object) opts
readonly
Returns the value of attribute opts.
-
- (Object) twitter
readonly
Returns the value of attribute twitter.
-
- (Object) xmpp
readonly
Returns the value of attribute xmpp.
Instance Method Summary (collapse)
-
- (Object) answer(text, user = nil)
A generic “respond immediately” interface.
-
- (Object) concurrently(req, &blk)
Wraps Thread.new with error handling and response pushing for the given request.
-
- (Object) dispatch(resp)
Performs appropriate dispatch operation for response type.
-
- (Object) dispatch_error(resp, e)
Records an error in dispatch and emits a corresponding :dispatch_error event.
-
- (Object) enable_twitter(opts = {})
Initialises Twitter-related functionality.
-
- (Object) enable_xmpp(options = {})
Initialises XMPP-related functionality.
-
- (Bot) initialize(options = {})
constructor
A new instance of Bot.
-
- (Object) process
Goes through each unprocessed Request and submits it for processing.
-
- (Object) process_request(req)
Processes an individual request (synchronously).
-
- (Object) pull
This pulls requests from passive non-streaming sources (currently, the Twitter AJAX API).
-
- (Object) push
Dispatches all undispatched Responses.
-
- (Object) request_error(req, e)
Records an error in processing and emits a corresponding :request_error event.
-
- (Object) respond(req, text)
Adds a response to the queue and dispatches it.
-
- (Object) run
Calls run_nowait and then waits for all child processes.
-
- (Object) run_nowait
Runs an initial request-processing loop and then forks the streaming processes.
-
- (Object) stop
Shuts down the streaming processes.
Methods included from Evented
#bind, #emit, #on, #once, #unbind
Constructor Details
- (Bot) initialize(options = {})
A new instance of Bot
264 265 266 267 268 269 270 |
# File 'lib/safubot/bot.rb', line 264 def initialize(={}) defaults = { :database => "safubot" } @opts = defaults.merge() MongoMapper.database = @opts[:database] MongoMapper.connection = Mongo::Connection.new('localhost', 27017, :pool_size => 5) @handlers = {} end |
Instance Attribute Details
- (Object) opts (readonly)
Returns the value of attribute opts
81 82 83 |
# File 'lib/safubot/bot.rb', line 81 def opts @opts end |
- (Object) twitter (readonly)
Returns the value of attribute twitter
81 82 83 |
# File 'lib/safubot/bot.rb', line 81 def twitter @twitter end |
- (Object) xmpp (readonly)
Returns the value of attribute xmpp
81 82 83 |
# File 'lib/safubot/bot.rb', line 81 def xmpp @xmpp end |
Instance Method Details
- (Object) answer(text, user = nil)
A generic “respond immediately” interface.
65 66 67 68 69 70 71 |
# File 'lib/safubot/test_helper.rb', line 65 def answer(text, user=nil) user ||= KnownUser.by_name('testing') query = Query.create(:user => user, :text => text) req = query.make_request process_request(req) req.responses.first.text end |
- (Object) concurrently(req, &blk)
Wraps Thread.new with error handling and response pushing for the given request.
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/safubot/bot.rb', line 211 def concurrently(req, &blk) Thread.new do begin blk.call rescue Exception => e request_error(req, e) end push end end |
- (Object) dispatch(resp)
Performs appropriate dispatch operation for response type.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/safubot/bot.rb', line 138 def dispatch(resp) resp.reload if resp.dispatched Log.debug "Response '#{resp.text}' has already been dispatched, ignoring." return elsif resp.dispatching Log.debug "Response '#{resp.text}' is already in dispatch, ignoring." return elsif resp.problems.length > 10 Log.debug "Response '#{resp.text}' encountered more than ten dispatch errors, ignoring." return elsif !resp.problems.empty? && (Time.now - resp.last_problem.when) < 1.minute Log.debug "Response '#{resp.text}' encountered a dispatch error <1 minute ago, ignoring." return end begin source = resp.request.source resp.dispatching = true resp.save if Safubot::mode != :production Log.info "#{source.class} Response to #{source.username}: #{resp.text}" else if @twitter && [Twitter::Tweet, Twitter::DirectMessage].include?(source.class) @twitter.send(resp) elsif @xmpp && [XMPP::Message].include?(source.class) @xmpp.send(resp) else raise NotImplementedError, "Don't know how to send response to a #{source.class}!" end resp.dispatched = true resp.save end rescue Exception => e dispatch_error(resp, e) ensure resp.dispatching = false resp.save end end |
- (Object) dispatch_error(resp, e)
Records an error in dispatch and emits a corresponding :dispatch_error event.
98 99 100 101 102 103 |
# File 'lib/safubot/bot.rb', line 98 def dispatch_error(resp, e) Log.error "Error dispatching #{resp.request.source.class} '#{resp.text}': #{error_report(e)}" resp.add_problem(e) resp.save emit(:dispatch_error, resp, e) end |
- (Object) enable_twitter(opts = {})
Initialises Twitter-related functionality.
246 247 248 249 250 251 252 |
# File 'lib/safubot/bot.rb', line 246 def enable_twitter(opts={}) @twitter = Twitter::Bot.new(opts) @twitter.on(:request) do |req| process_request(req) req.responses.where(:dispatched => false).map(&:dispatch) end end |
- (Object) enable_xmpp(options = {})
Initialises XMPP-related functionality.
255 256 257 258 259 260 261 262 |
# File 'lib/safubot/bot.rb', line 255 def enable_xmpp(={}) defaults = { :jid => nil, :password => nil } @xmpp = XMPP::Bot.new(defaults.merge()) @xmpp.on(:request) do |req| process_request(req) req.responses.where(:dispatched => false).map(&:dispatch) end end |
- (Object) process
Goes through each unprocessed Request and submits it for processing.
187 188 189 190 191 |
# File 'lib/safubot/bot.rb', line 187 def process Request.where(:processed => false).each do |req| concurrently(req) { process_request(req) } end end |
- (Object) process_request(req)
Processes an individual request (synchronously).
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/safubot/bot.rb', line 108 def process_request(req) req.reload if req.processed Log.debug "Request '#{req.text}' has already been processed, ignoring." return elsif req.processing Log.debug "Request '#{req.text}' is currently in processing, ignoring." return end begin req.processing = true req.save emit(:request, req) rescue Exception => e request_error(req, e) else req.success = true ensure #if Safubot::mode == :production req.processing = false req.processed = true req.save #end end end |
- (Object) pull
This pulls requests from passive non-streaming sources (currently, the Twitter AJAX API).
182 183 184 |
# File 'lib/safubot/bot.rb', line 182 def pull @twitter.pull end |
- (Object) push
Dispatches all undispatched Responses.
203 204 205 |
# File 'lib/safubot/bot.rb', line 203 def push Response.where(:dispatched => false).each(&method(:dispatch)) end |
- (Object) request_error(req, e)
Records an error in processing and emits a corresponding :request_error event.
87 88 89 90 91 92 |
# File 'lib/safubot/bot.rb', line 87 def request_error(req, e) Log.error "Error processing #{req.source.class} '#{req.text}': #{error_report(e)}" req.add_problem(e) req.save emit(:request_error, req, e) end |
- (Object) respond(req, text)
Adds a response to the queue and dispatches it.
197 198 199 200 |
# File 'lib/safubot/bot.rb', line 197 def respond(req, text) Log.info("#{req.user.name}: #{req.text}\nsafubot: #{text}") dispatch(Response.create(:request => req, :text => text)) end |
- (Object) run
Calls run_nowait and then waits for all child processes.
230 231 232 233 234 235 236 237 |
# File 'lib/safubot/bot.rb', line 230 def run run_nowait begin Process.waitall rescue Interrupt stop end end |
- (Object) run_nowait
Runs an initial request-processing loop and then forks the streaming processes.
223 224 225 226 227 |
# File 'lib/safubot/bot.rb', line 223 def run_nowait pull; process; push @twitter.run if @twitter @xmpp.run if @xmpp end |
- (Object) stop
Shuts down the streaming processes.
240 241 242 243 |
# File 'lib/safubot/bot.rb', line 240 def stop @twitter.stop if @twitter @xmpp.stop if @xmpp end |