{q: rooto:"YARD::CodeObjects::RootObject:@childrenIC:&YARD::CodeObjects::CodeObjectList[o:$YARD::CodeObjects::ModuleObject;IC;[o:$YARD::CodeObjects::MethodObject: @scope: class:@visibility: public: @pathI"Safubot.mode:EF:@parameters[: @files[[I"lib/safubot.rb;Ti:@current_file_has_commentsF: @name: mode:@source_type: ruby: @tags[:@docstringIC:YARD::Docstring"(Returns the value of attribute mode ;F: @object@ : @summary0:@hash_flagF:@ref_tags[;[: @allI"(Returns the value of attribute mode;F:@namespace@: @sourceI"def mode @mode end;F:@signatureI" def mode;F:@docstring_extra0: @dynamicTo; ; ; ; ;;I"Safubot.mode=;F;[[I" value;F0;[[@i;F;: mode=;;;[;IC;"Sets the attribute mode ;F;@;0;F;[;[o:YARD::Tags::Tag :@tag_nameI" param;F: @textI",the value to set the attribute mode to.;F;I" value;F: @types0;@;I"QSets the attribute mode @param value the value to set the attribute mode to.;F; @;!I")def mode=(value) @mode = value end;F;"I"def mode=(value);F;#0;$To; ; : instance; ;;I"Safubot#error_report;F;[["e0;[[I"lib/safubot/bot.rb;Ti ;T;:error_report;;;[;IC;"8Pretty-printing of Exceptions and their backtraces. ;F;@);0;T;[;[o;& ;'I" param;F;(I"An Exception to print.;F;I"e;F;)0;@);I"Y Pretty-printing of Exceptions and their backtraces. @param e An Exception to print.;F:@line_rangeo: Range: exclF: begini :endi ; @;!I"Idef error_report(e) "#{e.inspect}\n#{e.backtrace.join("\n\t")}" end;F;"I"def error_report(e);F;#0;$T:@explicitTo:#YARD::CodeObjects::ClassObject;IC;[: @owner@=:@class_mixinsIC;[;3@=:@instance_mixinsIC;[o:YARD::CodeObjects::Proxy : @imethod0:@origname""MongoMapper::EmbeddedDocument:@orignamespace@=;:EmbeddedDocument; o;6 ;70;80;90;:MongoMapper; @=: @obj0;<0;3@=:@attributesIC:SymbolHash{; IC;>{:@symbolize_valueT;*IC;>{;?T;?T: @aliases{: @groups[;[[@0i;T;: Problem;;; ;;[;IC;"@An EmbeddedDocument for storing processing/dispatch errors. ;F;@=;0;F;[;[;I"A An EmbeddedDocument for storing processing/dispatch errors.;F;,o;-;.F;/i;0i; @;I"Safubot::Problem;F:@superclasso;6 ;70;80;90;: Object; @;<0;#0;$To; ;IC;[o; ; ;*; ;;I"%Safubot::Problematic#add_problem;F;[["e0;[[@0i ;T;:add_problem;;;[;IC;",Adds a timestamped Problem to the list. ;F;@U;0;T;[;[o;& ;'I" param;F;(I"1Exception from which the Problem is derived.;F;I"e;F;)0;@U;I"c Adds a timestamped Problem to the list. @param e Exception from which the Problem is derived.;F;,o;-;.F;/i;0i; @S;!I"def add_problem(e) problem = Problem.new(:error => e.to_s, :type => e.class.to_s, :when => Time.now, :backtrace => e.backtrace) self.problems.push(problem) end;F;"I"def add_problem(e);F;#0;$T;1To; ; ;*; ;;I"&Safubot::Problematic#last_problem;F;[;[[@0i(;T;:last_problem;;;[;IC;"%Fetches the most recent Problem. ;F;@h;0;T;[;[;I"& Fetches the most recent Problem.;F;,o;-;.F;/i&;0i'; @S;!I"Odef last_problem self.problems.sort { |x,y| x.when <=> y.when }.last end;F;"I"def last_problem( );F;#0;$T;1T;3@S;4IC;[;3@S;5IC;[;3@S;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@0i;T;:Problematic;;; ;;[;IC;"%A mixin adding Problem handling. ;F;@S;0;T;[;[;I"& A mixin adding Problem handling.;F;,o;-;.F;/i;0i; @;I"Safubot::Problematic;F;#0;$To;2;IC;[;3@;4IC;[;3@;5IC;[o;6 ;70;80;90;;G; @;<@So;6 ;70;8"MongoMapper::Document;9@;: Document; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@0i1[I"lib/safubot/xmpp.rb;Ti[I"lib/safubot/twitter.rb;Ti*;T;: Request;;; ;;[;IC;"wDefines elements of the input queue, agnostic of the transfer medium. May be extended by service-specific modules. ;F;@;0;T;[;[;I"x Defines elements of the input queue, agnostic of the transfer medium. May be extended by service-specific modules.;F;,o;-;.F;/i.;0i0; @;I"Safubot::Request;F;Co;6 ;70;80;90;;D; @;<0;#0;$To;2;IC;[;3@;4IC;[;3@;5IC;[o;6 ;70;80;90;;G; @;<@So;6 ;70;8"MongoMapper::Document;9@;;H; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@0iC;T;: Response;;; ;;[;IC;"xDefines elements of the output queue, agnostic of the transfer medium. May be extended by service-specific modules. ;F;@;0;T;[;[;I"y Defines elements of the output queue, agnostic of the transfer medium. May be extended by service-specific modules.;F;,o;-;.F;/i@;0iB; @;I"Safubot::Response;F;Co;6 ;70;80;90;;D; @;<0;#0;$To;2;IC;[o; ; ;*; ;;I"Safubot::Bot#opts;F;[;[[@0iV;F;: opts;;;[;IC;"(Returns the value of attribute opts ;F;@;0;F;[;[;I"(Returns the value of attribute opts;F; @;!I"def opts @opts end;F;"I" def opts;F;#0;$To; ; ;*; ;;I"Safubot::Bot#twitter;F;[;[[@0iV;F;: twitter;;;[;IC;"+Returns the value of attribute twitter ;F;@;0;F;[;[;I"+Returns the value of attribute twitter;F; @;!I"def twitter @twitter end;F;"I"def twitter;F;#0;$To; ; ;*; ;;I"Safubot::Bot#xmpp;F;[;[[@0iV;F;: xmpp;;;[;IC;"(Returns the value of attribute xmpp ;F;@;0;F;[;[;I"(Returns the value of attribute xmpp;F; @;!I"def xmpp @xmpp end;F;"I" def xmpp;F;#0;$To; ; ;*; ;;I"Safubot::Bot#request_error;F;[["req0["e0;[[@0i\;T;:request_error;;;[;IC;"SRecords an error in processing and emits a corresponding :request_error event. ;F;@;0;T;[;[o;& ;'I" param;F;(I"5The Request for which the error was encountered.;F;I"req;F;)0;@o;& ;'I" param;F;(I"The caught Exception.;F;I"e;F;)0;@;I" Records an error in processing and emits a corresponding :request_error event. @param req The Request for which the error was encountered. @param e The caught Exception.;F;,o;-;.F;/iX;0i[; @;!I"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;F;"I"def request_error(req, e);F;#0;$T;1To; ; ;*; ;;I" Safubot::Bot#dispatch_error;F;[[" resp0["e0;[[@0ig;T;:dispatch_error;;;[;IC;"RRecords an error in dispatch and emits a corresponding :dispatch_error event. ;F;@;0;T;[;[o;& ;'I" param;F;(I"6The Response for which the error was encountered.;F;I" resp;F;)0;@o;& ;'I" param;F;(I"The caught Exception.;F;I"e;F;)0;@;I" Records an error in dispatch and emits a corresponding :dispatch_error event. @param resp The Response for which the error was encountered. @param e The caught Exception.;F;,o;-;.F;/ic;0if; @;!I"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;F;"I" def dispatch_error(resp, e);F;#0;$T;1To; ; ;*; ;;I"!Safubot::Bot#process_request;F;[["req0;[[@0iq;T;:process_request;;;[;IC;"5Processes an individual request (synchronously). ;F;@ ;0;T;[;[o;& ;'I" param;F;(I"An unprocessed Request.;F;I"req;F;)0;@ ;I"Y Processes an individual request (synchronously). @param req An unprocessed Request.;F;,o;-;.F;/in;0ip; @;!I"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;F;"I"def process_request(req);F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#dispatch;F;[[" resp0;[[@0i;T;: dispatch;;;[;IC;"?Performs appropriate dispatch operation for response type. ;F;@;0;T;[;[o;& ;'I" param;F;(I"An undispatched Response.;F;I" resp;F;)0;@;I"f Performs appropriate dispatch operation for response type. @param resp An undispatched Response.;F;,o;-;.F;/i;0i; @;!I"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;F;"I"def dispatch(resp);F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#pull;F;[;[[@0i;T;: pull;;;[;IC;"^This pulls requests from passive non-streaming sources (currently, the Twitter AJAX API). ;F;@0;0;F;[;[;I"^This pulls requests from passive non-streaming sources (currently, the Twitter AJAX API).;F;,o;-;.F;/i;0i; @;!I"!def pull @twitter.pull end;F;"I"def pull( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#process;F;[;[[@0i;T;: process;;;[;IC;"IGoes through each unprocessed Request and submits it for processing. ;F;@=;0;F;[;[;I"IGoes through each unprocessed Request and submits it for processing.;F;,o;-;.F;/i;0i; @;!I"ydef process Request.where(:processed => false).each do |req| concurrently(req) { process_request(req) } end end;F;"I"def process( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#respond;F;[["req0[" text0;[[@0i;T;: respond;;;[;IC;"4Adds a response to the queue and dispatches it. ;F;@J;0;T;[;[o;& ;'I" param;F;(I"Request to respond to.;F;I"req;F;)0;@Jo;& ;'I" param;F;(I"Contents of the response.;F;I" text;F;)0;@J;I"} Adds a response to the queue and dispatches it. @param req Request to respond to. @param text Contents of the response.;F;,o;-;.F;/i;0i; @;!I"def respond(req, text) Log.info("#{req.user.name}: #{req.text}\nsafubot: #{text}") dispatch(Response.create(:request => req, :text => text)) end;F;"I"def respond(req, text);F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#push;F;[;[[@0i;T;: push;;;[;IC;"+Dispatches all undispatched Responses. ;F;@c;0;F;[;[;I"+Dispatches all undispatched Responses.;F;,o;-;.F;/i;0i; @;!I"Qdef push Response.where(:dispatched => false).each(&method(:dispatch)) end;F;"I"def push( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#concurrently;F;[["req0[I" &blk;F0;[[@0i;T;:concurrently;;;[;IC;"UWraps Thread.new with error handling and response pushing for the given request. ;F;@p;0;T;[;[o;& ;'I" param;F;(I"!The Request being processed.;F;I"req;F;)0;@po;& ;'I" param;F;(I"8The operation to be performed in a separate thread.;F;I"blk;F;)0;@p;I" Wraps Thread.new with error handling and response pushing for the given request. @param req The Request being processed. @param blk The operation to be performed in a separate thread.;F;,o;-;.F;/i;0i; @;!I"def concurrently(req, &blk) Thread.new do begin blk.call rescue Exception => e request_error(req, e) end push end end;F;"I" def concurrently(req, &blk);F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#run_nowait;F;[;[[@0i;T;:run_nowait;;;[;IC;"TRuns an initial request-processing loop and then forks the streaming processes. ;F;@;0;F;[;[;I"TRuns an initial request-processing loop and then forks the streaming processes.;F;,o;-;.F;/i;0i; @;!I"]def run_nowait pull; process; push @twitter.run if @twitter @xmpp.run if @xmpp end;F;"I"def run_nowait( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#run;F;[;[[@0i;T;:run;;;[;IC;"=Calls run_nowait and then waits for all child processes. ;F;@;0;F;[;[;I"=Calls run_nowait and then waits for all child processes.;F;,o;-;.F;/i;0i; @;!I"Udef run run_nowait begin Process.waitall rescue Interrupt stop end end;F;"I"def run( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#stop;F;[;[[@0i;T;: stop;;;[;IC;"(Shuts down the streaming processes. ;F;@;0;F;[;[;I"(Shuts down the streaming processes.;F;,o;-;.F;/i;0i; @;!I"Cdef stop @twitter.stop if @twitter @xmpp.stop if @xmpp end;F;"I"def stop( );F;#0;$T;1To; ; ;*; ;;I" Safubot::Bot#enable_twitter;F;[[" opts"{};[[@0i;T;:enable_twitter;;;[;IC;"/Initialises Twitter-related functionality. ;F;@;0;F;[;[;I"/Initialises Twitter-related functionality.;F;,o;-;.F;/i;0i; @;!I"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;F;"I" def enable_twitter(opts={});F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#enable_xmpp;F;[[" options"{};[[@0i;T;:enable_xmpp;;;[;IC;",Initialises XMPP-related functionality. ;F;@;0;F;[;[;I",Initialises XMPP-related functionality.;F;,o;-;.F;/i;0i; @;!I"def enable_xmpp(options={}) defaults = { :jid => nil, :password => nil } @xmpp = XMPP::Bot.new(defaults.merge(options)) @xmpp.on(:request) do |req| process_request(req) req.responses.where(:dispatched => false).map(&:dispatch) end end;F;"I" def enable_xmpp(options={});F;#0;$T;1To; ; ;*; ;;I"Safubot::Bot#initialize;F;[[" options"{};[[@0i;F;:initialize;;;[;IC;" ;F;@;0;F;[;[o;& ;'I" return;F;(I"a new instance of Bot;F;0;)[I"Bot;F;@;I";F; @;!I"def initialize(options={}) defaults = { :database => "safubot" } @opts = defaults.merge(options) MongoMapper.database = @opts[:database] MongoMapper.connection = Mongo::Connection.new('localhost', 27017, :pool_size => 5) @handlers = {} end;F;"I"def initialize(options={});F;$T;1To; ; ;*; ;;I"Safubot::Bot#answer;F;[[" text0[" user"nil;[[I"lib/safubot/test_helper.rb;TiF;T;: answer;;;[;IC;"/A generic "respond immediately" interface. ;F;@;0;F;[;[o;& ;'I" param;F;(I"&The body of a Request to process.;F;I" text;F;)0;@o;& ;'I" param;F;(I"KThe user sending the request. Defaults to KnownUser.by_name('testing');F;I" user;F;)0;@;I"A generic "respond immediately" interface. @param text The body of a Request to process. @param user The user sending the request. Defaults to KnownUser.by_name('testing');F;,o;-;.F;/iC;0iE; @;!I"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;F;"I"def answer(text, user=nil);F;#0;$T;1T;3@;4IC;[;3@;5IC;[o;6 ;70;80;90;: Evented; @; Symbol, :id => Symbol, :repeat => Boolean, :proc => Proc } See Evented#on and Evented#once for sugar. ;F;@;0;T;[;[;I" Binds an event handler, which is a Hash of the form: { :evid => Symbol, :id => Symbol, :repeat => Boolean, :proc => Proc } See Evented#on and Evented#once for sugar.;F;,o;-;.F;/i ;0i; @;!I"def bind(evid, handler) @handlers ||= {} @handlers[evid] ||= {} handler[:evid] = evid handler[:id] ||= @handlers[evid].length @handlers[evid][handler[:id]] = handler end;F;"I"def bind(evid, handler);F;#0;$T;1To; ; ;*; ;;I"Safubot::Evented#unbind;F;[[" handler0;[[@ i;T;: unbind;;;[;IC;"Unbinds the given handler. ;F;@;0;T;[;[;I" Unbinds the given handler.;F;,o;-;.F;/i;0i; @;!I"Mdef unbind(handler) @handlers[handler[:evid]].delete(handler[:id]) end;F;"I"def unbind(handler);F;#0;$T;1To; ; ;*; ;;I"Safubot::Evented#on;F;[[" evid0[" opts"{}[I"&callback;F0;[[@ i!;T;:on;;;[;IC;"HBinds a block to the specified event id. Returns the bound handler. ;F;@%;0;T;[;[;I"I Binds a block to the specified event id. Returns the bound handler.;F;,o;-;.F;/i;0i ; @;!I"jdef on(evid, opts={}, &callback) bind(evid, { :repeat => true, :proc => callback }.merge(opts)) end;F;"I"%def on(evid, opts={}, &callback);F;#0;$T;1To; ; ;*; ;;I"Safubot::Evented#once;F;[[" evid0[" opts"{}[I"&callback;F0;[[@ i(;T;: once;;;[;IC;"bBinds a block to the specified event id, to be executed only once. Returns the bound handler. ;F;@9;0;T;[;[;I"c Binds a block to the specified event id, to be executed only once. Returns the bound handler.;F;,o;-;.F;/i%;0i'; @;!I"mdef once(evid, opts={}, &callback) bind(evid, { :repeat => false, :proc => callback }.merge(opts)) end;F;"I"'def once(evid, opts={}, &callback);F;#0;$T;1To; ; ;*; ;;I"Safubot::Evented#emit;F;[[" evid0[I" *args;F0;[[@ i.;T;: emit;;;[;IC;"2Fires an evid event with the given arguments. ;F;@M;0;T;[;[;I"3 Fires an evid event with the given arguments.;F;,o;-;.F;/i,;0i-; @;!I"def emit(evid, *args) return unless @handlers && @handlers[evid] @handlers[evid].each do |id, handler| begin handler[:proc].call(*args) ensure @handlers[evid].delete(id) if !handler[:repeat] end end end;F;"I"def emit(evid, *args);F;#0;$T;1T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@ i ;T;;^;;; ;;[;IC;">A mixin granting simple event firing and handler binding. ;F;@;0;T;[;[;I"? A mixin granting simple event firing and handler binding.;F;,o;-;.F;/i;0i; @;I"Safubot::Evented;F;#0;$T;3@;=IC;>{; IC;>{;?T;*IC;>{;KIC;>{: read@: write0;?T;LIC;>{;d@;e0;?T;MIC;>{;d@;e0;?T;?T;?T;@{;A[;[[@0iS[@iB;T;:Bot;;; ;;[;IC;"The main event-processing class. You are encouraged to inherit from this class when building your own bot, but delegation is also entirely feasible. ;F;@;0;T;[;[;I" The main event-processing class. You are encouraged to inherit from this class when building your own bot, but delegation is also entirely feasible.;F;,o;-;.F;/iO;0iR; @;I"Safubot::Bot;F;Co;6 ;70;80;90;;D; @;<0;#0;$To; ;IC;[o; ; ; ; ;;I"Safubot::Log.path;F;[;[[I"lib/safubot/log.rb;Ti ;F;: path;;;[;IC;"(Returns the value of attribute path ;F;@;0;F;[;[;I"(Returns the value of attribute path;F; @;!I"def path @path end;F;"I" def path;F;#0;$To; ; ; ; ;;I"Safubot::Log.path=;F;[["str0;[[@i;T;: path=;;;[;IC;"1Commence logging to the specified file path. ;F;@;0;F;[;[;I"1Commence logging to the specified file path.;F;,o;-;.F;/i;0i; @;!I"Bdef path=(str) @path = str @filelog = Logger.new(str) end;F;"I"def path=(str);F;#0;$T;1To; ; ; ; ;;I" Safubot::Log.method_missing;F;[[" method0[I" *args;F0;[[@i;T;:method_missing;;;[;IC;"-Dispatch method calls to Logger objects. ;F;@;0;F;[;[;I"-Dispatch method calls to Logger objects.;F;,o;-;.F;/i;0i; @;!I"def method_missing(method, *args) @stdlog ||= Logger.new(STDOUT) @stdlog.send(method, *args) @filelog.send(method, *args) if @filelog end;F;"I"&def method_missing(method, *args);F;#0;$T;1T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;gIC;>{;d@;e@;?T;?T;*IC;>{;?T;?T;@{;A[;[[@i ;T;:Log;;; ;;[;IC;"JLog is a simple wrapper for stdout and file-writing Logger instances. ;F;@;0;F;[;[;I"JLog is a simple wrapper for stdout and file-writing Logger instances.;F;,o;-;.F;/i ;0i ; @;I"Safubot::Log;F;#0;$To;2;IC;[ o; ; ; ; ;;I"Safubot::KnownUser.by_xmpp;F;[["jid0;[[@i;T;: by_xmpp;;;[;IC;" jid).first || KnownUser.create(:jid => jid, :name => jid.split('@')[0]) end;F;"I"def by_xmpp(jid);F;#0;$T;1To; ; ; ; ;;I""Safubot::KnownUser.by_twitter;F;[["name_or_id0;[[@i;T;:by_twitter;;;[;IC;"HRetrieve or construct a KnownUser by the given Twitter screen name. ;F;@;0;F;[;[o;& ;'I" param;F;(I"6The Twitter screen_name (String) or id (Integer).;F;I"name_or_id;F;)0;@;I"Retrieve or construct a KnownUser by the given Twitter screen name. @param name_or_id The Twitter screen_name (String) or id (Integer).;F;,o;-;.F;/i;0i; @;!I"<def by_twitter(name_or_id) user = if name_or_id.is_a? String KnownUser.where('twitter.screen_name' => name_or_id).first else KnownUser.where('twitter.id' => name_or_id).first end if user.nil? details = ::Twitter.user(name_or_id) # This second lookup is necessary as the screen_name that comes back from # Twitter.user might not be the same as the one we were originally provided # with. user = KnownUser.where('twitter.screen_name' => details.screen_name).first || KnownUser.create(:twitter => details.attrs, :name => details.screen_name) end user end;F;"I"def by_twitter(name_or_id);F;#0;$T;1To; ; ;*; ;;I"Safubot::KnownUser#tweets;F;[;[[@i%;T;: tweets;;;[;IC;"?Plucky query with Tweets scoped to this user's twitter id. ;F;@;0;F;[;[;I"?Plucky query with Tweets scoped to this user's twitter id.;F;,o;-;.F;/i$;0i$; @;!I"Odef tweets Twitter::Tweet.where('raw.user.id' => self.twitter['id']) end;F;"I"def tweets( );F;#0;$T;1To; ; ;*; ;;I"Safubot::KnownUser#merge;F;[[" user0;[[I"lib/safubot/known_user.rb;Ti;T;: merge;;;[;IC;"gCombines the service-specific identities of two users to form a cohesive picture of an individual. ;F;@;0;F;[;[;I"gCombines the service-specific identities of two users to form a cohesive picture of an individual.;F;,o;-;.F;/i;0i; @;!I"Adef merge(user) self.keys.keys.each do |key| Log.info "#{key.inspect} #{self[key].inspect} #{user[key].inspect}" self[key] = user[key] if self[key].nil? || (self[key].respond_to?(:empty?) && self[key].empty?) end user.requests.each do |req| req.user = self; req.save! end save! user.destroy end;F;"I"def merge(user);F;#0;$T;1To; ; ; ; ;;I"Safubot::KnownUser.by_name;F;[[" name0;[[@i;T;: by_name;;;[;IC;"+Retrieves user by internal identifier. ;F;@;0;F;[;[;I"+Retrieves user by internal identifier.;F;,o;-;.F;/i;0i; @;!I"edef by_name(name) KnownUser.where(:name => name).first || KnownUser.create(:name => name) end;F;"I"def by_name(name);F;#0;$T;1T;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;H; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[ [@i [@i [@i [@i ;T;:KnownUser;;; ;;[;IC;"General identity cache. Extended by service-specific modules. A single class in order to allow linking of identity between services. ;F;@;0;F;[;[;I"General identity cache. Extended by service-specific modules. A single class in order to allow linking of identity between services.;F;,o;-;.F;/i;0i; @;I"Safubot::KnownUser;F;Co;6 ;70;80;90;;D; @;<0;#0;$To; ;IC;[o;2;IC;[ o; ; ; ; ;;I" Safubot::XMPP::Message.from;F;[["raw0;[[@i1;T;: from;;;[;IC;"Creates a new Message. ;F;@,;0;F;[;[o;& ;'I" param;F;(I"2The Blather stanza from which to source data.;F;I"raw;F;)0;@,;I"TCreates a new Message. @param raw The Blather stanza from which to source data.;F;,o;-;.F;/i/;0i0; @*;!I"def from(raw) Message.find_by_xmpp_id(raw.id) || Message.create({ :xml => raw.to_xml, :xmpp_id => raw.id, :text => raw.body.strip, :to => raw.to.to_s, :from => raw.from.to_s }) end;F;"I"def from(raw);F;#0;$T;1To; ; ;*; ;;I"$Safubot::XMPP::Message#username;F;[;[[@i>;T;: username;;;[;IC;"2Retrieves the sender's username from the JID. ;F;@?;0;F;[;[;I"2Retrieves the sender's username from the JID.;F;,o;-;.F;/i=;0i=; @*;!I"/def username self.from.split('@')[0] end;F;"I"def username( );F;#0;$T;1To; ; ;*; ;;I" Safubot::XMPP::Message#user;F;[;[[@iC;T;: user;;;[;IC;":Retrieves the KnownUser associated with this Message. ;F;@L;0;F;[;[;I":Retrieves the KnownUser associated with this Message.;F;,o;-;.F;/iB;0iB; @*;!I"0def user KnownUser.by_xmpp(self.from) end;F;"I"def user( );F;#0;$T;1To; ; ;*; ;;I"(Safubot::XMPP::Message#make_request;F;[;[[@iH;T;:make_request;;;[;IC;"0Retrieves or creates an associated Request. ;F;@Y;0;F;[;[;I"0Retrieves or creates an associated Request.;F;,o;-;.F;/iG;0iG; @*;!I"ndef make_request self.request || Request.create(:user => user, :source => self, :text => self.text) end;F;"I"def make_request( );F;#0;$T;1T;3@*;4IC;[;3@*;5IC;[o;6 ;70;8"MongoMapper::Document;9@*;;H; o;6 ;70;80;90;;;; @*;<0;<0;3@*;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i ;T;: Message;;; ;;[;IC;"JA Message is a Request source representing a single XMPP chat stanza. ;F;@*;0;F;[;[;I"JA Message is a Request source representing a single XMPP chat stanza.;F;,o;-;.F;/i;0i; @(;I"Safubot::XMPP::Message;F;Co;6 ;70;80;90;;D; @;<0;#0;$To;2;IC;[o; ; ;*; ;;I"Safubot::XMPP::Bot#jid;F;[;[[@iP;F;:jid;;;[;IC;"'Returns the value of attribute jid ;F;@|;0;F;[;[;I"'Returns the value of attribute jid;F; @z;!I"def jid @jid end;F;"I" def jid;F;#0;$To; ; ;*; ;;I"Safubot::XMPP::Bot#client;F;[;[[@iP;F;: client;;;[;IC;"*Returns the value of attribute client ;F;@;0;F;[;[;I"*Returns the value of attribute client;F; @z;!I"def client @client end;F;"I"def client;F;#0;$To; ; ;*; ;;I"Safubot::XMPP::Bot#state;F;[;[[@iP;F;: state;;;[;IC;")Returns the value of attribute state ;F;@;0;F;[;[;I")Returns the value of attribute state;F; @z;!I"def state @state end;F;"I"def state;F;#0;$To; ; ;*; ;;I"Safubot::XMPP::Bot#pid;F;[;[[@iP;F;:pid;;;[;IC;"'Returns the value of attribute pid ;F;@;0;F;[;[;I"'Returns the value of attribute pid;F; @z;!I"def pid @pid end;F;"I" def pid;F;#0;$To; ; ;*; ;;I"$Safubot::XMPP::Bot#init_blather;F;[;[[@iS;T;:init_blather;;;[;IC;"6Sets our Blather::Client event processor running. ;F;@;0;F;[;[;I"6Sets our Blather::Client event processor running.;F;,o;-;.F;/iR;0iR; @z;!I"udef init_blather @client = Blather::Client.setup(@jid, @password) @client.register_handler(:ready) do Log.info "XMPP client is online at #{@client.jid.stripped} :D" emit(:ready) end @client.register_handler(:subscription, :request?) do |s| Log.info "Approving subscription request from: #{s.from}" @client.write s.approve! end @client.register_handler(:message, :chat?, :body) do |msg| unless msg.body.match(/^\?OTR:/) emit(:request, Message.from(msg).make_request) end end @client.register_handler(:disconnected) do sleep 1 # HACK (Mispy): Give the state a chance to change when we're stopped. if @state == :running Log.warn("XMPP disconnected; attempting reconnection in 5 seconds.") sleep 5; @client.connect end end @client.register_handler(:error) do |e| Log.error "Unhandled Blather error: #{error_report(e)}" end @state = :running end;F;"I"def init_blather( );F;#0;$T;1To; ; ;*; ;;I"#Safubot::XMPP::Bot#run_blather;F;[;[[@iw;T;:run_blather;;;[;IC;"Runs the Blather client. ;F;@;0;F;[;[;I"Runs the Blather client.;F;,o;-;.F;/iv;0iv; @z;!I"@def run_blather begin EM::run { @client.run } rescue Exception => e if e.is_a?(Interrupt) || e.is_a?(SignalException) stop elsif @state == :running Log.error "XMPP client exited unexpectedly: #{error_report(e)}" Log.error "Restarting XMPP client in 5 seconds." sleep 5; init_blather; run_blather end end end;F;"I"def run_blather( );F;#0;$T;1To; ; ;*; ;;I"Safubot::XMPP::Bot#run;F;[;[[@i;T;;X;;;[;IC;"8Starts our Blather client running in a new process. ;F;@;0;F;[;[;I"8Starts our Blather client running in a new process.;F;,o;-;.F;/i;0i; @z;!I"hdef run @pid = Process.fork do Signal.trap("TERM") { stop } init_blather run_blather end end;F;"I"def run( );F;#0;$T;1To; ; ;*; ;;I"Safubot::XMPP::Bot#stop;F;[;[[@i;T;;Y;;;[;IC;"#Shuts down the Blather client. ;F;@;0;F;[;[;I"#Shuts down the Blather client.;F;,o;-;.F;/i;0i; @z;!I"def stop if @client @state = :stopped @client.close @client = nil Log.info "XMPP client shutdown complete." elsif @pid Process.kill("TERM", @pid) end end;F;"I"def stop( );F;#0;$T;1To; ; ;*; ;;I"Safubot::XMPP::Bot#tell;F;[["jid0[" text0;[[@i;F;: tell;;;[;IC;" ;F;@;0;F;[;[;I";F; @z;!I"vdef tell(jid, text) msg = Blather::Stanza::Message.new msg.to = jid msg.body = text @client.write msg end;F;"I"def tell(jid, text);F;$T;1To; ; ;*; ;;I"Safubot::XMPP::Bot#send;F;[[" resp0;[[@i;T;: send;;;[;IC;""Dispatch a Response via XMPP. ;F;@;0;F;[;[;I""Dispatch a Response via XMPP.;F;,o;-;.F;/i;0i; @z;!I"}def send(resp) if @state == :running tell(resp.request.source.from, resp.text) else on(:ready) { send(resp) } end end;F;"I"def send(resp);F;#0;$T;1To; ; ;*; ;;I""Safubot::XMPP::Bot#initialize;F;[[" opts0;[[@i;F;;\;;;[;IC;" ;F;@;0;F;[;[o;& ;'I" return;F;(I"a new instance of Bot;F;0;)[I"Bot;F;@;I";F; @z;!I"cdef initialize(opts) @jid = opts[:jid] @password = opts[:password] @state = :stopped end;F;"I"def initialize(opts);F;$T;1T;3@z;4IC;[;3@z;5IC;[o;6 ;70;80;90;;^; @;<@;3@z;=IC;>{; IC;>{;?T;*IC;>{ ;vIC;>{;d@|;e0;?T;wIC;>{;d@;e0;?T;xIC;>{;d@;e0;?T;yIC;>{;d@;e0;?T;?T;?T;@{;A[;[[@iM;F;;f;;; ;;[;IC;" ;F;@z;0;F;[;[;I";F; @(;I"Safubot::XMPP::Bot;F;Co;6 ;70;80;90;;D; @;<0;$T;3@(;4IC;[;3@(;5IC;[;3@(;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i;T;: XMPP;;; ;;[;IC;"!XMPP-specific functionality. ;F;@(;0;F;[;[;I"!XMPP-specific functionality.;F;,o;-;.F;/i;0i; @;I"Safubot::XMPP;F;#0;$T@o:&YARD::CodeObjects::ConstantObject;[[I"lib/safubot/version.rb;Ti;F;: VERSION;;; ;;[;IC;" ;F;@7;0;F;[;[;I";F; @;I"Safubot::VERSION;F: @linei;""VERSION = "0.0.4";!I"VERSION = "0.0.4";F: @valueI" "0.0.4";F;$To; ;IC;[o;2;IC;[o; ; ; ; ;;I"Safubot::Twitter::Tweet.[];F;[["id0;[[@iB;T;:[];;;[;IC;"Quickly look up a Tweet. ;F;@H;0;T;[;[o;& ;'I" param;F;(I"The Twitter id.;F;I"id;F;)0;@H;I"8 Quickly look up a Tweet. @param id The Twitter id.;F;,o;-;.F;/i?;0iA; @F;!I"7def [](id) Tweet.where('raw.id' => id).first end;F;"I"def [](id);F;#0;$T;1To; ; ; ; ;;I"!Safubot::Twitter::Tweet.from;F;[[" status0;[[@iI;T;;q;;;[;IC;"Find or create a Tweet. ;F;@[;0;T;[;[o;& ;'I" param;F;(I"6An appropriate ::Twitter or ::TweetStream object.;F;I" status;F;)0;@[;I"] Find or create a Tweet. @param status An appropriate ::Twitter or ::TweetStream object.;F;,o;-;.F;/iF;0iH; @F;!I"def from(status) Tweet.where('raw.id' => status['id']||status.id).first || Tweet.create(:raw => status['attrs']||status) end;F;"I"def from(status);F;#0;$T;1To; ; ;*; ;;I"!Safubot::Twitter::Tweet#text;F;[;[[@iP;T;: text;;;[;IC;"The plain text. ;F;@n;0;F;[;[;I"The plain text.;F;,o;-;.F;/iO;0iO; @F;!I"$def text self.raw['text'] end;F;"I"def text( );F;#0;$T;1To; ; ;*; ;;I",Safubot::Twitter::Tweet#targetless_text;F;[;[[@iU;T;:targetless_text;;;[;IC;"-Plain text without the leading mentions. ;F;@{;0;F;[;[;I"-Plain text without the leading mentions.;F;,o;-;.F;/iT;0iT; @F;!I"9def targetless_text text.gsub(/^(@\w+ )+/, '') end;F;"I"def targetless_text( );F;#0;$T;1To; ; ;*; ;;I",Safubot::Twitter::Tweet#header_mentions;F;[;[[@iZ;T;:header_mentions;;;[;IC;":Retrieve a substring containing the leading mentions. ;F;@;0;F;[;[;I":Retrieve a substring containing the leading mentions.;F;,o;-;.F;/iY;0iY; @F;!I"Tdef header_mentions m = text.match(/(@\w+ )+/) m.nil? ? [] : m[0].split end;F;"I"def header_mentions( );F;#0;$T;1To; ; ;*; ;;I"+Safubot::Twitter::Tweet#original_tweet;F;[;[[@i`;T;:original_tweet;;;[;IC;"CReturns the original tweet of which this is a RT if available. ;F;@;0;F;[;[;I"CReturns the original tweet of which this is a RT if available.;F;,o;-;.F;/i_;0i_; @F;!I"fdef original_tweet self.raw['retweeted_status'] && Tweet.from(self.raw['retweeted_status']) end;F;"I"def original_tweet( );F;#0;$T;1To; ; ;*; ;;I"!Safubot::Twitter::Tweet#user;F;[;[[@ie;T;;s;;;[;IC;":Finds or creates a KnownUser connected to this Tweet. ;F;@;0;F;[;[;I":Finds or creates a KnownUser connected to this Tweet.;F;,o;-;.F;/id;0id; @F;!I"@def user KnownUser.by_twitter(self.raw['user']['id']) end;F;"I"def user( );F;#0;$T;1To; ; ;*; ;;I"%Safubot::Twitter::Tweet#username;F;[;[[@ij;T;;r;;;[;IC;""Finds the user's screen_name. ;F;@;0;F;[;[;I""Finds the user's screen_name.;F;,o;-;.F;/ii;0ii; @F;!I"3def username user.twitter['screen_name'] end;F;"I"def username( );F;#0;$T;1To; ; ;*; ;;I"&Safubot::Twitter::Tweet#posted_at;F;[;[[@io;T;:posted_at;;;[;IC;">Returns a Time object specifying the time of publication. ;F;@;0;F;[;[;I">Returns a Time object specifying the time of publication.;F;,o;-;.F;/in;0in; @F;!I";def posted_at Time.parse(self.raw['created_at']) end;F;"I"def posted_at( );F;#0;$T;1To; ; ;*; ;;I")Safubot::Twitter::Tweet#make_request;F;[;[[@it;T;;t;;;[;IC;"8Finds or creates a Request sourced from this Tweet. ;F;@;0;F;[;[;I"8Finds or creates a Request sourced from this Tweet.;F;,o;-;.F;/is;0is; @F;!I"ydef make_request self.request || Request.create(:user => user, :source => self, :text => self.targetless_text) end;F;"I"def make_request( );F;#0;$T;1T;3@F;4IC;[;3@F;5IC;[o;6 ;70;8"MongoMapper::Document;9@F;;H; o;6 ;70;80;90;;;; @F;<0;<0;3@F;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i4;T;: Tweet;;; ;;[;IC;"Tweet is both a Request source and general-purpose tweet storage. Mentions are automatically made into Requests, but not timeline tweets. ;F;@F;0;F;[;[;I"Tweet is both a Request source and general-purpose tweet storage. Mentions are automatically made into Requests, but not timeline tweets.;F;,o;-;.F;/i2;0i3; @D;I"Safubot::Twitter::Tweet;F;Co;6 ;70;80;90;;D; @;<0;#0;$To;2;IC;[ o; ; ; ; ;;I")Safubot::Twitter::DirectMessage.from;F;[[" message0;[[@i;T;;q;;;[;IC;"$Find or create a DirectMessage. ;F;@;0;T;[;[o;& ;'I" param;F;(I"6An appropriate ::Twitter or ::TweetStream object.;F;I" message;F;)0;@;I"f Find or create a DirectMessage. @param message An appropriate ::Twitter or ::TweetStream object.;F;,o;-;.F;/i;0i; @;!I"def from(message) DirectMessage.where('raw.id' => message['id']||message.id).first || DirectMessage.create(:raw => message['attrs']||message) end;F;"I"def from(message);F;#0;$T;1To; ; ;*; ;;I")Safubot::Twitter::DirectMessage#text;F;[;[[@i;T;;;;;[;IC;"The plain text. ;F;@;0;F;[;[;I"The plain text.;F;,o;-;.F;/i;0i; @;!I"$def text self.raw['text'] end;F;"I"def text( );F;#0;$T;1To; ; ;*; ;;I"-Safubot::Twitter::DirectMessage#username;F;[;[[@i;T;;r;;;[;IC;""Finds the user's screen_name. ;F;@ ;0;F;[;[;I""Finds the user's screen_name.;F;,o;-;.F;/i;0i; @;!I"9def username self.raw['sender']['screen_name'] end;F;"I"def username( );F;#0;$T;1To; ; ;*; ;;I")Safubot::Twitter::DirectMessage#user;F;[;[[@i;T;;s;;;[;IC;"BFinds or creates a KnownUser connected to this DirectMessage. ;F;@;0;F;[;[;I"BFinds or creates a KnownUser connected to this DirectMessage.;F;,o;-;.F;/i;0i; @;!I"2def user KnownUser.by_twitter(username) end;F;"I"def user( );F;#0;$T;1To; ; ;*; ;;I"1Safubot::Twitter::DirectMessage#make_request;F;[;[[@i;T;;t;;;[;IC;"@Finds or creates a Request sourced from this DirectMessage. ;F;@&;0;F;[;[;I"@Finds or creates a Request sourced from this DirectMessage.;F;,o;-;.F;/i;0i; @;!I"ndef make_request self.request || Request.create(:user => user, :source => self, :text => self.text) end;F;"I"def make_request( );F;#0;$T;1T;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;H; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i{;T;:DirectMessage;;; ;;[;IC;"gDirectMessage is a Request source. All DirectMessages are made into Requests as they are received. ;F;@;0;F;[;[;I"hDirectMessage is a Request source. All DirectMessages are made into Requests as they are received. ;F;,o;-;.F;/iy;0iz; @D;I"$Safubot::Twitter::DirectMessage;F;Co;6 ;70;80;90;;D; @;<0;#0;$To;2;IC;[o; ; ;*; ;;I"#Safubot::Twitter::Bot#username;F;[;[[@i;F;;r;;;[;IC;",Returns the value of attribute username ;F;@I;0;F;[;[;I",Returns the value of attribute username;F; @G;!I"!def username @username end;F;"I"def username;F;#0;$To; ; ;*; ;;I"!Safubot::Twitter::Bot#client;F;[;[[@i;F;;w;;;[;IC;"*Returns the value of attribute client ;F;@U;0;F;[;[;I"*Returns the value of attribute client;F; @G;!I"def client @client end;F;"I"def client;F;#0;$To; ; ;*; ;;I"Safubot::Twitter::Bot#opts;F;[;[[@i;F;;K;;;[;IC;"(Returns the value of attribute opts ;F;@a;0;F;[;[;I"(Returns the value of attribute opts;F; @G;!I"def opts @opts end;F;"I" def opts;F;#0;$To; ; ;*; ;;I"!Safubot::Twitter::Bot#stream;F;[;[[@i;F;: stream;;;[;IC;"*Returns the value of attribute stream ;F;@m;0;F;[;[;I"*Returns the value of attribute stream;F; @G;!I"def stream @stream end;F;"I"def stream;F;#0;$To; ; ;*; ;;I"Safubot::Twitter::Bot#pid;F;[;[[@i;F;;y;;;[;IC;"'Returns the value of attribute pid ;F;@y;0;F;[;[;I"'Returns the value of attribute pid;F; @G;!I"def pid @pid end;F;"I" def pid;F;#0;$To; ; ;*; ;;I"Safubot::Twitter::Bot#send;F;[[" resp0;[[@i;T;;};;;[;IC;"@Sends a Twitter-sourced Response to the appropriate target. ;F;@;0;T;[;[o;& ;'I" param;F;(I"Response to send.;F;I" resp;F;)0;@;I"_ Sends a Twitter-sourced Response to the appropriate target. @param resp Response to send.;F;,o;-;.F;/i;0i; @G;!I";def send(resp) source = resp.request.source if source.is_a?(DirectMessage) @client.direct_message_create(source.raw['sender']['screen_name'], resp.text) elsif source.is_a?(Tweet) reply source, resp.text else raise NotImplementedError, "Don't know how to send response to a #{req.source.class}!" end end;F;"I"def send(resp);F;#0;$T;1To; ; ;*; ;;I")Safubot::Twitter::Bot#handle_request;F;[["req0;[[@i;T;:handle_request;;;[;IC;"BEmit a request event unless the request is already processed. ;F;@;0;T;[;[o;& ;'I" param;F;(I"Request to handle.;F;I"req;F;)0;@;I"a Emit a request event unless the request is already processed. @param req Request to handle.;F;,o;-;.F;/i;0i; @G;!I"Wdef handle_request(req) emit(:request, req) unless req.nil? || req.processed end;F;"I"def handle_request(req);F;#0;$T;1To; ; ;*; ;;I")Safubot::Twitter::Bot#handle_message;F;[[" message0;[[@i;T;:handle_message;;;[;IC;":Stores a DM and creates a matching Request as needed. ;F;@;0;T;[;[o;& ;'I" param;F;(I"'A raw JSON-derived direct message.;F;I" message;F;)0;@;I"m Stores a DM and creates a matching Request as needed. @param message A raw JSON-derived direct message.;F;,o;-;.F;/i;0i; @G;!I"def handle_message(message) return if message.sender.screen_name == @username handle_request(DirectMessage.from(message).make_request) end;F;"I" def handle_message(message);F;#0;$T;1To; ; ;*; ;;I"'Safubot::Twitter::Bot#handle_tweet;F;[[" status0;[[@i;T;:handle_tweet;;;[;IC;"sStores a tweet. If this tweet is directed at us, create a matching Request. Otherwise, emit a :timeline event. ;F;@;0;T;[;[o;& ;'I" param;F;(I"A raw JSON-derived tweet.;F;I" status;F;)0;@;I" Stores a tweet. If this tweet is directed at us, create a matching Request. Otherwise, emit a :timeline event. @param status A raw JSON-derived tweet.;F;,o;-;.F;/i;0i; @G;!I"def handle_tweet(status) return if status.user.screen_name == @username if status.text.match(/@#{@username}/i) handle_request(Tweet.from(status).make_request) else emit(:timeline, Tweet.from(status)) end end;F;"I"def handle_tweet(status);F;#0;$T;1To; ; ;*; ;;I"'Safubot::Twitter::Bot#reply_header;F;[[" tweet0;[[@i;T;:reply_header;;;[;IC;"MConstructs the appropriate series of mentions for a reply to this tweet. ;F;@;0;F;[;[;I"MConstructs the appropriate series of mentions for a reply to this tweet.;F;,o;-;.F;/i;0i; @G;!I"ndef reply_header(tweet) (["@#{tweet.username}"] + (tweet.header_mentions - ["@#{@username}"])).join end;F;"I"def reply_header(tweet);F;#0;$T;1To; ; ;*; ;;I" Safubot::Twitter::Bot#reply;F;[[" tweet0[" text0;[[@i;T;: reply;;;[;IC;"7Replies to a tweet using the appropriate mentions. ;F;@;0;F;[;[o;& ;'I" param;F;(I""A Tweet object to respond to.;F;I" tweet;F;)0;@o;& ;'I" param;F;(I"The response text.;F;I" text;F;)0;@;I"|Replies to a tweet using the appropriate mentions. @param tweet A Tweet object to respond to. @param text The response text.;F;,o;-;.F;/i;0i; @G;!I"}def reply(tweet, text) @client.update("#{reply_header(tweet)} #{text}", :in_reply_to_status_id => tweet.raw['id']) end;F;"I"def reply(tweet, text);F;#0;$T;1To; ; ;*; ;;I"Safubot::Twitter::Bot#pull;F;[;[[@i;T;;R;;;[;IC;"Pulls DMs and mentions using the AJAX API. Used in tandem with the streaming API to ensure we don't miss too much while we're offline. ;F;@;0;F;[;[;I"Pulls DMs and mentions using the AJAX API. Used in tandem with the streaming API to ensure we don't miss too much while we're offline.;F;,o;-;.F;/i;0i; @G;!I"vdef pull begin @client.direct_messages.each do |message| handle_message(message) end @client.mentions.each do |mention| handle_tweet(mention) end rescue ::Twitter::Error::ServiceUnavailable Log.error "Twitter: Couldn't pull tweets due to temporary service unavailability." rescue Exception => e Log.error "Twitter: Unhandled error: #{error_report(e)}" end end;F;"I"def pull( );F;#0;$T;1To; ; ;*; ;;I"&Safubot::Twitter::Bot#init_stream;F;[;[[@i;T;:init_stream;;;[;IC;"(Initializes the TweetStream client. ;F;@;0;F;[;[;I"(Initializes the TweetStream client.;F;,o;-;.F;/i;0i; @G;!I"def init_stream @stream = TweetStream::Client.new(@opts) @stream.on_direct_message do |message| handle_message(message) end @stream.on_error do |err| if err.match(/invalid status code: 401/) Log.error "TweetStream authentication failure!" else Log.error "Unhandled TweetStream error: #{error_report($!)}" end end @stream.on_inited do Log.info("TweetStream client is online at @#{@username} :3") emit(:ready) end end;F;"I"def init_stream( );F;#0;$T;1To; ; ;*; ;;I"%Safubot::Twitter::Bot#run_stream;F;[;[[@i;T;:run_stream;;;[;IC;"!Runs the TweetStream client. ;F;@;0;F;[;[;I"!Runs the TweetStream client.;F;,o;-;.F;/i;0i; @G;!I"Udef run_stream begin @stream.userstream do |status| handle_tweet(status) end rescue Exception => e if e.is_a?(Interrupt) || e.is_a?(SignalException) stop else Log.error "TweetStream client exited unexpectedly: #{error_report(e)}" Log.error "Restarting TweetStream client in 5 seconds." sleep 5; init_stream; run_stream end end end;F;"I"def run_stream( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Twitter::Bot#run;F;[;[[@i;T;;X;;;[;IC;" nil, :consumer_key => nil, :consumer_secret => nil, :oauth_token => nil, :oauth_token_secret => nil, :auth_method => :oauth } @opts = defaults.merge(options) DirectMessage.ensure_index('raw.id', :unique => true) @username = @opts[:username] @client = Object::Twitter::Client.new(@opts) end;F;"I"def initialize(options={});F;#0;$T;1T;3@G;4IC;[;3@G;5IC;[o;6 ;70;80;90;;^; @;<@;3@G;=IC;>{; IC;>{;?T;*IC;>{ ;rIC;>{;d@I;e0;?T;wIC;>{;d@U;e0;?T;KIC;>{;d@a;e0;?T;IC;>{;d@m;e0;?T;yIC;>{;d@y;e0;?T;?T;?T;@{;A[;[[@i;T;;f;;; ;;[;IC;"VA Twitter::Bot instance provides a Safubot::Bot with Twitter-specific processing. ;F;@G;0;F;[;[;I"VA Twitter::Bot instance provides a Safubot::Bot with Twitter-specific processing.;F;,o;-;.F;/i;0i; @D;I"Safubot::Twitter::Bot;F;Co;6 ;70;80;90;;D; @;<0;#0;$T;3@D;4IC;[;3@D;5IC;[;3@D;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i1;T;: Twitter;;; ;;[;IC;"!XMPP-specific functionality. ;F;@D;0;F;[;[;I"!XMPP-specific functionality.;F;,o;-;.F;/i0;0i0; @;I"Safubot::Twitter;F;#0;$To; ;IC;[o; ; ; ; ;;I"$Safubot::Test.clean_environment;F;[;[[@i;T;:clean_environment;;;[;IC;"DSwitches to the "safubot_testing" database and clears all data. ;F;@|;0;F;[;[;I"DSwitches to the "safubot_testing" database and clears all data.;F;,o;-;.F;/i;0i; @z;!I"def clean_environment MongoMapper.database = "safubot_testing" Request.destroy_all Query.destroy_all Response.destroy_all KnownUser.destroy_all Safubot::mode = :testing $bot = Safubot::Bot.new(:database => "safubot_testing") end;F;"I"def clean_environment( );F;#0;$T;1To; ; ; ; ;;I"Safubot::Test.request;F;[[" text0;[[@i';T;: request;;;[;IC;"-Creates a Query-sourced testing Request. ;F;@;0;F;[;[o;& ;'I" param;F;(I"The body of the Request.;F;I" text;F;)0;@;I"RCreates a Query-sourced testing Request. @param text The body of the Request.;F;,o;-;.F;/i%;0i&; @z;!I"ldef request(text) Query.create(:user => KnownUser.by_name('testing'), :text => text).make_request end;F;"I"def request(text);F;#0;$T;1T;3@z;4IC;[;3@z;5IC;[;3@z;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i;T;: Test;;; ;;[;IC;"9Helpful methods for testing either via RSpec or IRB. ;F;@z;0;F;[;[;I"9Helpful methods for testing either via RSpec or IRB.;F;,o;-;.F;/i;0i; @;I"Safubot::Test;F;#0;$To;2;IC;[o; ; ;*; ;;I" Safubot::Query#make_request;F;[;[[@i9;T;;t;;;[;IC;"6Find or create a Request sourced from this Query. ;F;@;0;F;[;[;I"6Find or create a Request sourced from this Query.;F;,o;-;.F;/i8;0i8; @;!I"tdef make_request self.request || Request.create(:user => self.user, :text => self.text, :source => self) end;F;"I"def make_request( );F;#0;$T;1To; ; ;*; ;;I"Safubot::Query#username;F;[;[[@i=;F;;r;;;[;IC;" ;F;@;0;F;[;[;I";F; @;!I"&def username self.user.name end;F;"I"def username( );F;$T;1T;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;H; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[[@i.;T;: Query;;; ;;[;IC;"'Generic mediumless Request source. ;F;@;0;F;[;[;I"'Generic mediumless Request source.;F;,o;-;.F;/i-;0i-; @;I"Safubot::Query;F;Co;6 ;70;80;90;;D; @;<0;#0;$T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;IC;>{;d@ ;e@;?T;?T;*IC;>{;?T;?T;@{;A[;[[@i[@0i[@i[@i [@ i[@:i[@i [@i[@i;F;: Safubot;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I" Safubot;F;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;*IC;>{;?T;?T;@{;A[;[;F;;;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; 0;I";F;@:Safubot.mode@ :Safubot.mode=@:Safubot#error_report@):Safubot::Problem@=:Safubot::Problematic@S:%Safubot::Problematic#add_problem@U:&Safubot::Problematic#last_problem@h:Safubot::Request@:Safubot::Response@:Safubot::Bot@:Safubot::Bot#opts@:Safubot::Bot#twitter@:Safubot::Bot#xmpp@:Safubot::Bot#request_error@: Safubot::Bot#dispatch_error@:!Safubot::Bot#process_request@ :Safubot::Bot#dispatch@:Safubot::Bot#pull@0:Safubot::Bot#process@=:Safubot::Bot#respond@J:Safubot::Bot#push@c:Safubot::Bot#concurrently@p:Safubot::Bot#run_nowait@:Safubot::Bot#run@:Safubot::Bot#stop@: Safubot::Bot#enable_twitter@:Safubot::Bot#enable_xmpp@:Safubot::Bot#initialize@:Safubot::Log@:Safubot::Log.path@:Safubot::Log.path=@: Safubot::Log.method_missing@:Safubot::KnownUser@:Safubot::KnownUser.by_xmpp@:Safubot::XMPP@(:Safubot::XMPP::Message@*: Safubot::XMPP::Message.from@,:$Safubot::XMPP::Message#username@?: Safubot::XMPP::Message#user@L:(Safubot::XMPP::Message#make_request@Y:Safubot::XMPP::Bot@z:Safubot::XMPP::Bot#jid@|:Safubot::XMPP::Bot#client@:Safubot::XMPP::Bot#state@:Safubot::XMPP::Bot#pid@:$Safubot::XMPP::Bot#init_blather@:#Safubot::XMPP::Bot#run_blather@:Safubot::XMPP::Bot#run@:Safubot::XMPP::Bot#stop@:Safubot::XMPP::Bot#tell@:Safubot::XMPP::Bot#send@:"Safubot::XMPP::Bot#initialize@:Safubot::Evented@:Safubot::Evented#bind@:Safubot::Evented#unbind@:Safubot::Evented#on@%:Safubot::Evented#once@9:Safubot::Evented#emit@M:Safubot::VERSION@7:"Safubot::KnownUser.by_twitter@:Safubot::KnownUser#tweets@:Safubot::Twitter@D:Safubot::Twitter::Tweet@F:Safubot::Twitter::Tweet.[]@H:!Safubot::Twitter::Tweet.from@[:!Safubot::Twitter::Tweet#text@n:,Safubot::Twitter::Tweet#targetless_text@{:,Safubot::Twitter::Tweet#header_mentions@:+Safubot::Twitter::Tweet#original_tweet@:!Safubot::Twitter::Tweet#user@:%Safubot::Twitter::Tweet#username@:&Safubot::Twitter::Tweet#posted_at@:)Safubot::Twitter::Tweet#make_request@:$Safubot::Twitter::DirectMessage@:)Safubot::Twitter::DirectMessage.from@:)Safubot::Twitter::DirectMessage#text@:-Safubot::Twitter::DirectMessage#username@ :)Safubot::Twitter::DirectMessage#user@:1Safubot::Twitter::DirectMessage#make_request@&:Safubot::Twitter::Bot@G:#Safubot::Twitter::Bot#username@I:!Safubot::Twitter::Bot#client@U:Safubot::Twitter::Bot#opts@a:!Safubot::Twitter::Bot#stream@m:Safubot::Twitter::Bot#pid@y:Safubot::Twitter::Bot#send@:)Safubot::Twitter::Bot#handle_request@:)Safubot::Twitter::Bot#handle_message@:'Safubot::Twitter::Bot#handle_tweet@:'Safubot::Twitter::Bot#reply_header@: Safubot::Twitter::Bot#reply@:Safubot::Twitter::Bot#pull@:&Safubot::Twitter::Bot#init_stream@:%Safubot::Twitter::Bot#run_stream@:Safubot::Twitter::Bot#run@ :Safubot::Twitter::Bot#stop@-:%Safubot::Twitter::Bot#initialize@::Safubot::KnownUser#merge@:Safubot::KnownUser.by_name@:Safubot::Test@z:$Safubot::Test.clean_environment@|:Safubot::Test.request@:Safubot::Query@: Safubot::Query#make_request@:Safubot::Query#username@:Safubot::Bot#answer@