{d: 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:#YARD::CodeObjects::ClassObject;IC;[o; ; : instance; ;;I"Safubot::Request#callback;F;[;[[I"lib/safubot/bot.rb;Ti;T;: callback;;;[;IC;"LIf a callback is set, responses will be sent to it instead of Response. ;F;@+;0;F;[;[;I"LIf a callback is set, responses will be sent to it instead of Response.;F:@line_rangeo: Range: exclF: begini:endi; @);!I"!def callback @callback end;F;"I"def callback;F;#0;$To; ; ;+; ;;I"Safubot::Request#callback=;F;[[I" value;F0;[[@0i;T;:callback=;;;[;IC;"LIf a callback is set, responses will be sent to it instead of Response. ;F;@9;0;F;[;[;@5;-@6; @);!I"1def callback=(value) @callback = value end;F;"I"def callback=(value);F;#0;$T: @owner@):@class_mixinsIC;[;3@):@instance_mixinsIC;[o:YARD::CodeObjects::Proxy : @imethod0:@origname"MongoMapper::Document:@orignamespace@);: Document; o;6 ;70;80;90;:MongoMapper; @): @obj0;<0;3@):@attributesIC:SymbolHash{; IC;>{:@symbolize_valueT;+IC;>{;,IC;>{: read@+: write@9;?T;?T;?T: @aliases{: @groups[;[[@0i [I"lib/safubot/xmpp.rb;Ti[I"lib/safubot/twitter.rb;Ti+;T;: Request;;; ;;[;IC;"GExtend the Request class with Twitter-related source_type scoping. ;F;@);0;F;[;[;I"GExtend the Request class with Twitter-related source_type scoping.;F;-o;.;/F;0i*;1i*; @;I"Safubot::Request;F:@superclasso;6 ;70;80;90;: Object; @;<0;#0;$To;*;IC;[;3@_;4IC;[;3@_;5IC;[o;6 ;70;8"MongoMapper::Document;9@_;;:; o;6 ;70;80;90;;;; @_;<0;<0;3@_;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@0i;T;: Response;;; ;;[;IC;"xDefines elements of the output queue, agnostic of the transfer medium. May be extended by service-specific modules. ;F;@_;0;F;[;[;I"xDefines elements of the output queue, agnostic of the transfer medium. May be extended by service-specific modules.;F;-o;.;/F;0i;1i; @;I"Safubot::Response;F;Eo;6 ;70;80;90;;F; @;<0;#0;$To;*;IC;[o; ; ;+; ;;I"Safubot::Bot#opts;F;[;[[@0i+;F;: opts;;;[;IC;"(Returns the value of attribute opts ;F;@w;0;F;[;[;I"(Returns the value of attribute opts;F; @u;!I"def opts @opts end;F;"I" def opts;F;#0;$To; ; ;+; ;;I"Safubot::Bot#twitter;F;[;[[@0i+;F;: twitter;;;[;IC;"+Returns the value of attribute twitter ;F;@~;0;F;[;[;I"+Returns the value of attribute twitter;F; @u;!I"def twitter @twitter end;F;"I"def twitter;F;#0;$To; ; ;+; ;;I"Safubot::Bot#xmpp;F;[;[[@0i+;F;: xmpp;;;[;IC;"(Returns the value of attribute xmpp ;F;@;0;F;[;[;I"(Returns the value of attribute xmpp;F; @u;!I"def xmpp @xmpp end;F;"I" def xmpp;F;#0;$To; ; ;+; ;;I"Safubot::Bot#request_error;F;[["req0["e0;[[@0i-;F;:request_error;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"def request_error(req, e) Log.error "Error processing #{req.source.class} '#{req.text}': #{e}\n#{e.backtrace.join("\n\t")}" req.errors[Time.now] = e req.save emit(:request_error, req, e) end;F;"I"def request_error(req, e);F;$T:@explicitTo; ; ;+; ;;I"!Safubot::Bot#process_request;F;[["req0;[[@0i4;F;:process_request;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"def process_request(req) begin emit(:request, req) rescue Exception => e request_error(req, e) ensure req.processed = true req.save end end;F;"I"def process_request(req);F;$T;LTo; ; ;+; ;;I"Safubot::Bot#dispatch;F;[[" resp0;[[@0i?;F;: dispatch;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"def dispatch(resp) # Performs appropriate dispatch operation for response type. begin source = resp.request.source 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 end resp.dispatched = true resp.save rescue Exception => e Log.error "Error dispatching #{source.class} '#{resp.text}': #{e}" resp.errors[Time.now] = e resp.save emit(:dispatch_error, resp, e) end end;F;"I"def dispatch(resp);F;$T;LTo; ; ;+; ;;I"Safubot::Bot#pull;F;[;[[@0iY;F;: pull;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"!def pull @twitter.pull end;F;"I"def pull( );F;$T;LTo; ; ;+; ;;I"Safubot::Bot#process;F;[;[[@0i];F;: process;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"ydef process Request.where(:processed => false).each do |req| concurrently(req) { process_request(req) } end end;F;"I"def process( );F;$T;LTo; ; ;+; ;;I"Safubot::Bot#respond;F;[["req0[" text0;[[@0if;T;: respond;;;[;IC;""Adds a response to the queue. ;F;@;0;F;[;[o;& ;'I" param;F;(I"Request to respond to.;F;I"req;F;)0;@o;& ;'I" param;F;(I"Contents of the response.;F;I" text;F;)0;@;I"jAdds a response to the queue. @param req Request to respond to. @param text Contents of the response.;F;-o;.;/F;0ic;1ie; @u;!I"def respond(req, text) Log.info("#{req.user.name}: #{req.text}\nsafubot: #{text}") Response.create(:request => req, :text => text) end;F;"I"def respond(req, text);F;#0;$T;LTo; ; ;+; ;;I"Safubot::Bot#respond_now;F;[["req0[" text0;[[@0il;T;:respond_now;;;[;IC;"Respond + push ;F;@;0;F;[;[;I"Respond + push;F;-o;.;/F;0ik;1ik; @u;!I"?def respond_now(req, text) respond(req, text) push end;F;"I"def respond_now(req, text);F;#0;$T;LTo; ; ;+; ;;I"Safubot::Bot#push;F;[;[[@0iq;F;: push;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"Qdef push Response.where(:dispatched => false).each(&method(:dispatch)) end;F;"I"def push( );F;$T;LTo; ; ;+; ;;I"Safubot::Bot#cycle;F;[;[[@0iu;F;: cycle;;;[;IC;" ;F;@;0;F;[;[;I";F; @u;!I"(def cycle pull; process; push end;F;"I"def cycle( );F;$T;LTo; ; ;+; ;;I"Safubot::Bot#concurrently;F;[["req0[I" &blk;F0;[[@0i|;T;:concurrently;;;[;IC;"TWraps EM::defer with error handling and response pushing for the given request. ;F;@;0;F;[;[o;& ;'I" param;F;(I"!The Request being processed.;F;I"req;F;)0;@o;& ;'I" param;F;(I"8The operation to be performed in a separate thread.;F;I"blk;F;)0;@;I"Wraps EM::defer 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;0iy;1i{; @u;!I"def concurrently(req, &blk) EventMachine::defer do begin blk.call rescue Exception => e request_error(req, e) end push end end;F;"I" def concurrently(req, &blk);F;#0;$T;LTo; ; ;+; ;;I"Safubot::Bot#run;F;[;[[@0i;F;:run;;;[;IC;" ;F;@5;0;F;[;[;I";F; @u;!I"#def run EventMachine::run do EM::error_handler do |e| if e.is_a? Interrupt Log.error("Received interrupt, shutting down.") stop else Log.error("Unhandled exception: #{e}\n#{e.backtrace.join("\t\n")}") end end cycle @twitter.run if @twitter @xmpp.run if @xmpp end end;F;"I"def run( );F;$T;LTo; ; ;+; ;;I"Safubot::Bot#stop;F;[;[[@0i;F;: stop;;;[;IC;" ;F;@A;0;F;[;[;I";F; @u;!I"Ydef stop @twitter.stop if @twitter @xmpp.stop if @xmpp EM::stop_event_loop end;F;"I"def stop( );F;$T;LTo; ; ;+; ;;I" Safubot::Bot#enable_twitter;F;[[" opts"{};[[@0i;F;:enable_twitter;;;[;IC;" ;F;@M;0;F;[;[;I";F; @u;!I"~def enable_twitter(opts={}) @twitter = Twitter::Bot.new(opts) @twitter.on(:request) do |req| process; push end end;F;"I" def enable_twitter(opts={});F;$T;LTo; ; ;+; ;;I"Safubot::Bot#enable_xmpp;F;[[" options"{};[[@0i;F;:enable_xmpp;;;[;IC;" ;F;@\;0;F;[;[;I";F; @u;!I"def enable_xmpp(options={}) defaults = { :jid => nil, :password => nil } @xmpp = XMPP::Bot.new(defaults.merge(options)) @xmpp.on(:request) do |req| process; push end end;F;"I" def enable_xmpp(options={});F;$T;LTo; ; ;+; ;;I"Safubot::Bot#initialize;F;[[" options"{};[[@0i;F;:initialize;;;[;IC;" ;F;@k;0;F;[;[o;& ;'I" return;F;(I"a new instance of Bot;F;0;)[I"Bot;F;@k;I";F; @u;!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;LTo; ; ;+; ;;I"Safubot::Bot#answer;F;[[" text0[" user"nil;[[I"lib/safubot/test_helper.rb;Ti;;T;: answer;;;[;IC;"/A generic "respond immediately" interface. ;F;@;0;F;[;[;I"/A generic "respond immediately" interface.;F;-o;.;/F;0i:;1i:; @u;!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;LT;3@u;4IC;[;3@u;5IC;[o;6 ;70;80;90;: Evented; @; Symbol, :id => Symbol, :repeat => Boolean, :proc => Proc } See {on}[rdoc-ref:Safubot::Evented#on] and {once}[rdoc-ref:Safubot::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 {on}[rdoc-ref:Safubot::Evented#on] and {once}[rdoc-ref:Safubot::Evented#once] for sugar.;F;-o;.;/F;0i ;1i; @;!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;LTo; ; ;+; ;;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;0i;1i; @;!I"Mdef unbind(handler) @handlers[handler[:evid]].delete(handler[:id]) end;F;"I"def unbind(handler);F;#0;$T;LTo; ; ;+; ;;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;0i;1i ; @;!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;LTo; ; ;+; ;;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;@;0;T;[;[;I"c Binds a block to the specified event id, to be executed only once. Returns the bound handler.;F;-o;.;/F;0i%;1i'; @;!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;LTo; ; ;+; ;;I"Safubot::Evented#emit;F;[[" evid0[I" *args;F0;[[@i.;T;: emit;;;[;IC;"2Fires an evid event with the given arguments. ;F;@;0;T;[;[;I"3 Fires an evid event with the given arguments.;F;-o;.;/F;0i,;1i-; @;!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;LT;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@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;0i;1i; @;I"Safubot::Evented;F;$T;#0;3@u;=IC;>{; IC;>{;?T;+IC;>{;HIC;>{;@@w;A0;?T;IIC;>{;@@~;A0;?T;JIC;>{;@@;A0;?T;?T;?T;B{;C[;[[@0i([@i9;F;:Bot;;; ;;[;IC;" ;F;@u;0;F;[;[;I";F; @;I"Safubot::Bot;F;Eo;6 ;70;80;90;;F; @;<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;0i ;1i ; @;!I"Bdef path=(str) @path = str @filelog = Logger.new(str) end;F;"I"def path=(str);F;#0;$T;LTo; ; ; ; ;;I" Safubot::Log.method_missing;F;[[" method0[I" *args;F0;[[@i;F;:method_missing;;;[;IC;" ;F;@1;0;F;[;[;I";F; @;!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;$T;LT;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;cIC;>{;@@;A@";?T;?T;+IC;>{;?T;?T;B{;C[;[[@i ;F;:Log;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I"Safubot::Log;F;$To;*;IC;[ o; ; ; ; ;;I"Safubot::KnownUser.by_xmpp;F;[["jid0;[[@Ti;T;: by_xmpp;;;[;IC;" jid).first || KnownUser.create(:jid => jid, :name => jid.split('@')[0]) end;F;"I"def by_xmpp(jid);F;#0;$T;LTo; ; ; ; ;;I""Safubot::KnownUser.by_twitter;F;[["name_or_id0;[[@Vi;T;:by_twitter;;;[;IC;"HRetrieve or construct a KnownUser by the given Twitter screen name. ;F;@b;0;F;[;[o;& ;'I" param;F;(I"6The Twitter screen_name (String) or id (Integer).;F;I"name_or_id;F;)0;@b;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;0i;1i; @Q;!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;LTo; ; ;+; ;;I"Safubot::KnownUser#tweets;F;[;[[@Vi%;T;: tweets;;;[;IC;"?Plucky query with Tweets scoped to this user's twitter id. ;F;@u;0;F;[;[;I"?Plucky query with Tweets scoped to this user's twitter id.;F;-o;.;/F;0i$;1i$; @Q;!I"Odef tweets Twitter::Tweet.where('raw.user.id' => self.twitter['id']) end;F;"I"def tweets( );F;#0;$T;LTo; ; ;+; ;;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;0i;1i; @Q;!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;LTo; ; ; ; ;;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;0i;1i; @Q;!I"edef by_name(name) KnownUser.where(:name => name).first || KnownUser.create(:name => name) end;F;"I"def by_name(name);F;#0;$T;LT;3@Q;4IC;[;3@Q;5IC;[o;6 ;70;8"MongoMapper::Document;9@Q;;:; o;6 ;70;80;90;;;; @Q;<0;<0;3@Q;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[ [@i [@Ti [@Vi [@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;@Q;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;0i;1i; @;I"Safubot::KnownUser;F;Eo;6 ;70;80;90;;F; @;<0;#0;$To; ;IC;[o;*;IC;[ o; ; ; ; ;;I" Safubot::XMPP::Message.from;F;[["raw0;[[@Ti0;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;0i.;1i/; @;!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;LTo; ; ;+; ;;I"$Safubot::XMPP::Message#username;F;[;[[@Ti=;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;0i<;1i<; @;!I"/def username self.from.split('@')[0] end;F;"I"def username( );F;#0;$T;LTo; ; ;+; ;;I" Safubot::XMPP::Message#user;F;[;[[@TiB;T;: user;;;[;IC;":Retrieves the KnownUser associated with this Message. ;F;@;0;F;[;[;I":Retrieves the KnownUser associated with this Message.;F;-o;.;/F;0iA;1iA; @;!I"0def user KnownUser.by_xmpp(self.from) end;F;"I"def user( );F;#0;$T;LTo; ; ;+; ;;I"(Safubot::XMPP::Message#make_request;F;[;[[@TiG;T;:make_request;;;[;IC;"0Retrieves or creates an associated Request. ;F;@;0;F;[;[;I"0Retrieves or creates an associated Request.;F;-o;.;/F;0iF;1iF; @;!I"ndef make_request self.request || Request.create(:user => user, :source => self, :text => self.text) end;F;"I"def make_request( );F;#0;$T;LT;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;:; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@Ti;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;0i;1i; @;I"Safubot::XMPP::Message;F;Eo;6 ;70;80;90;;F; @;<0;#0;$To;*;IC;[ o; ; ;+; ;;I"Safubot::XMPP::Bot#jid;F;[;[[@TiO;F;:jid;;;[;IC;"'Returns the value of attribute jid ;F;@ ;0;F;[;[;I"'Returns the value of attribute jid;F; @ ;!I"def jid @jid end;F;"I" def jid;F;#0;$To; ; ;+; ;;I"Safubot::XMPP::Bot#client;F;[;[[@TiO;F;: client;;;[;IC;"*Returns the value of attribute client ;F;@;0;F;[;[;I"*Returns the value of attribute client;F; @ ;!I"def client @client end;F;"I"def client;F;#0;$To; ; ;+; ;;I"Safubot::XMPP::Bot#state;F;[;[[@TiO;F;: state;;;[;IC;")Returns the value of attribute state ;F;@$;0;F;[;[;I")Returns the value of attribute state;F; @ ;!I"def state @state end;F;"I"def state;F;#0;$To; ; ;+; ;;I"Safubot::XMPP::Bot#run;F;[;[[@TiR;T;;V;;;[;IC;"6Sets our Blather::Client event processor running. ;F;@0;0;F;[;[;I"6Sets our Blather::Client event processor running.;F;-o;.;/F;0iQ;1iQ; @ ;!I"def run @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 if @state == :running Log.warn("XMPP disconnected; attempting reconnection in 5 seconds.") EM::Timer.new(5) { @client.connect } end end @client.register_handler(:error) do |e| Log.error "Unhandled Blather error: #{$!} #{$@}" end Fiber.new do begin @client.run rescue Exception => e if e.is_a? Interrupt raise e else Log.error "Blather client exited unexpectedly: #{$!} #{$@}" end end end.resume @state = :running end;F;"I"def run( );F;#0;$T;LTo; ; ;+; ;;I"Safubot::XMPP::Bot#stop;F;[;[[@Ti{;T;;W;;;[;IC;"#Shuts down the Blather client. ;F;@=;0;F;[;[;I"#Shuts down the Blather client.;F;-o;.;/F;0i;1i; @ ;!I"5def stop @state = :stopped @client.close end;F;"I"def stop( );F;#0;$T;LTo; ; ;+; ;;I"Safubot::XMPP::Bot#send;F;[[" resp0;[[@Ti;T;: send;;;[;IC;""Dispatch a Response via XMPP. ;F;@J;0;F;[;[;I""Dispatch a Response via XMPP.;F;-o;.;/F;0i;1i; @ ;!I"def send(resp) msg = Blather::Stanza::Message.new msg.to = resp.request.source.from msg.body = resp.text @client.write msg end;F;"I"def send(resp);F;#0;$T;LTo; ; ;+; ;;I""Safubot::XMPP::Bot#initialize;F;[[" opts0;[[@Ti;F;;Z;;;[;IC;" ;F;@Y;0;F;[;[o;& ;'I" return;F;(I"a new instance of Bot;F;0;)[I"Bot;F;@Y;I";F; @ ;!I"cdef initialize(opts) @jid = opts[:jid] @password = opts[:password] @state = :stopped end;F;"I"def initialize(opts);F;$T;LT;3@ ;4IC;[;3@ ;5IC;[o;6 ;70;80;90;;\; @;<@;3@ ;=IC;>{; IC;>{;?T;+IC;>{;rIC;>{;@@ ;A0;?T;sIC;>{;@@;A0;?T;tIC;>{;@@$;A0;?T;?T;?T;B{;C[;[[@TiL;F;;b;;; ;;[;IC;" ;F;@ ;0;F;[;[;I";F; @;I"Safubot::XMPP::Bot;F;Eo;6 ;70;80;90;;F; @;<0;$T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@Ti;F;: XMPP;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I"Safubot::XMPP;F;$T@o:&YARD::CodeObjects::ConstantObject;[[I"lib/safubot/version.rb;Ti;F;: VERSION;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I"Safubot::VERSION;F: @linei;""VERSION = "0.0.2";!I"VERSION = "0.0.2";F: @valueI" "0.0.2";F;$To; ;IC;[o;*;IC;[o; ; ; ; ;;I"Safubot::Twitter::Tweet.[];F;[["id0;[[@Vi@;T;:[];;;[;IC;"Quickly look up a Tweet. ;F;@;0;F;[;[o;& ;'I" param;F;(I"The Twitter id.;F;I"id;F;)0;@;I"7Quickly look up a Tweet. @param id The Twitter id.;F;-o;.;/F;0i>;1i?; @;!I"7def [](id) Tweet.where('raw.id' => id).first end;F;"I"def [](id);F;#0;$T;LTo; ; ; ; ;;I"!Safubot::Twitter::Tweet.from;F;[[" status0;[[@ViF;T;;m;;;[;IC;"Find or create a Tweet. ;F;@;0;F;[;[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;0iD;1iE; @;!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;LTo; ; ;+; ;;I"!Safubot::Twitter::Tweet#text;F;[;[[@ViM;T;: text;;;[;IC;"The plain text. ;F;@;0;F;[;[;I"The plain text.;F;-o;.;/F;0iL;1iL; @;!I"$def text self.raw['text'] end;F;"I"def text( );F;#0;$T;LTo; ; ;+; ;;I",Safubot::Twitter::Tweet#targetless_text;F;[;[[@ViR;T;:targetless_text;;;[;IC;"-Plain text without the leading mentions. ;F;@;0;F;[;[;I"-Plain text without the leading mentions.;F;-o;.;/F;0iQ;1iQ; @;!I"9def targetless_text text.gsub(/^(@\w+ )+/, '') end;F;"I"def targetless_text( );F;#0;$T;LTo; ; ;+; ;;I",Safubot::Twitter::Tweet#header_mentions;F;[;[[@ViW;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;0iV;1iV; @;!I"Tdef header_mentions m = text.match(/(@\w+ )+/) m.nil? ? [] : m[0].split end;F;"I"def header_mentions( );F;#0;$T;LTo; ; ;+; ;;I"+Safubot::Twitter::Tweet#original_tweet;F;[;[[@Vi];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;0i\;1i\; @;!I"fdef original_tweet self.raw['retweeted_status'] && Tweet.from(self.raw['retweeted_status']) end;F;"I"def original_tweet( );F;#0;$T;LTo; ; ;+; ;;I"!Safubot::Twitter::Tweet#user;F;[;[[@Vib;T;;o;;;[;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;0ia;1ia; @;!I"@def user KnownUser.by_twitter(self.raw['user']['id']) end;F;"I"def user( );F;#0;$T;LTo; ; ;+; ;;I"%Safubot::Twitter::Tweet#username;F;[;[[@Vig;T;;n;;;[;IC;""Finds the user's screen_name. ;F;@;0;F;[;[;I""Finds the user's screen_name.;F;-o;.;/F;0if;1if; @;!I"3def username user.twitter['screen_name'] end;F;"I"def username( );F;#0;$T;LTo; ; ;+; ;;I"&Safubot::Twitter::Tweet#posted_at;F;[;[[@Vil;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;0ik;1ik; @;!I";def posted_at Time.parse(self.raw['created_at']) end;F;"I"def posted_at( );F;#0;$T;LTo; ; ;+; ;;I")Safubot::Twitter::Tweet#make_request;F;[;[[@Viq;T;;p;;;[;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;0ip;1ip; @;!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;LT;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;:; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@Vi3;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;@;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;0i1;1i2; @;I"Safubot::Twitter::Tweet;F;Eo;6 ;70;80;90;;F; @;<0;#0;$To;*;IC;[ o; ; ; ; ;;I")Safubot::Twitter::DirectMessage.from;F;[[" message0;[[@Vi;T;;m;;;[;IC;"$Find or create a DirectMessage. ;F;@D;0;F;[;[o;& ;'I" param;F;(I"6An appropriate ::Twitter or ::TweetStream object.;F;I" message;F;)0;@D;I"eFind or create a DirectMessage. @param message An appropriate ::Twitter or ::TweetStream object.;F;-o;.;/F;0i~;1i; @B;!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;LTo; ; ;+; ;;I")Safubot::Twitter::DirectMessage#text;F;[;[[@Vi;T;;|;;;[;IC;"The plain text. ;F;@W;0;F;[;[;I"The plain text.;F;-o;.;/F;0i;1i; @B;!I"$def text self.raw['text'] end;F;"I"def text( );F;#0;$T;LTo; ; ;+; ;;I"-Safubot::Twitter::DirectMessage#username;F;[;[[@Vi;T;;n;;;[;IC;""Finds the user's screen_name. ;F;@d;0;F;[;[;I""Finds the user's screen_name.;F;-o;.;/F;0i;1i; @B;!I"9def username self.raw['sender']['screen_name'] end;F;"I"def username( );F;#0;$T;LTo; ; ;+; ;;I")Safubot::Twitter::DirectMessage#user;F;[;[[@Vi;T;;o;;;[;IC;"BFinds or creates a KnownUser connected to this DirectMessage. ;F;@q;0;F;[;[;I"BFinds or creates a KnownUser connected to this DirectMessage.;F;-o;.;/F;0i;1i; @B;!I"2def user KnownUser.by_twitter(username) end;F;"I"def user( );F;#0;$T;LTo; ; ;+; ;;I"1Safubot::Twitter::DirectMessage#make_request;F;[;[[@Vi;T;;p;;;[;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;0i;1i; @B;!I"ndef make_request self.request || Request.create(:user => user, :source => self, :text => self.text) end;F;"I"def make_request( );F;#0;$T;LT;3@B;4IC;[;3@B;5IC;[o;6 ;70;8"MongoMapper::Document;9@B;;:; o;6 ;70;80;90;;;; @B;<0;<0;3@B;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@Vix;T;:DirectMessage;;; ;;[;IC;"gDirectMessage is a Request source. All DirectMessages are made into Requests as they are received. ;F;@B;0;F;[;[;I"hDirectMessage is a Request source. All DirectMessages are made into Requests as they are received. ;F;-o;.;/F;0iv;1iw; @;I"$Safubot::Twitter::DirectMessage;F;Eo;6 ;70;80;90;;F; @;<0;#0;$To;*;IC;[o; ; ;+; ;;I"#Safubot::Twitter::Bot#username;F;[;[[@Vi;F;;n;;;[;IC;",Returns the value of attribute username ;F;@;0;F;[;[;I",Returns the value of attribute username;F; @;!I"!def username @username end;F;"I"def username;F;#0;$To; ; ;+; ;;I"!Safubot::Twitter::Bot#client;F;[;[[@Vi;F;;s;;;[;IC;"*Returns the value of attribute client ;F;@;0;F;[;[;I"*Returns the value of attribute client;F; @;!I"def client @client end;F;"I"def client;F;#0;$To; ; ;+; ;;I"Safubot::Twitter::Bot#opts;F;[;[[@Vi;F;;H;;;[;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::Twitter::Bot#send;F;[[" resp0;[[@Vi;T;;u;;;[;IC;"@Sends a Twitter-sourced Response to the appropriate target. ;F;@;0;F;[;[;I"@Sends a Twitter-sourced Response to the appropriate target.;F;-o;.;/F;0i;1i; @;!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.header_mentions.join(' ')} #{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;LTo; ; ;+; ;;I")Safubot::Twitter::Bot#handle_request;F;[["req0;[[@Vi;T;:handle_request;;;[;IC;"BEmit a request event unless the request is already processed. ;F;@;0;F;[;[;I"BEmit a request event unless the request is already processed.;F;-o;.;/F;0i;1i; @;!I"Wdef handle_request(req) emit(:request, req) unless req.nil? || req.processed end;F;"I"def handle_request(req);F;#0;$T;LTo; ; ;+; ;;I")Safubot::Twitter::Bot#handle_message;F;[[" message0;[[@Vi;T;:handle_message;;;[;IC;":Stores a DM and creates a matching Request as needed. ;F;@;0;F;[;[;I":Stores a DM and creates a matching Request as needed.;F;-o;.;/F;0i;1i; @;!I"~def handle_message(message) return if message.sender.screen_name == @username DirectMessage.from(message).make_request end;F;"I" def handle_message(message);F;#0;$T;LTo; ; ;+; ;;I"'Safubot::Twitter::Bot#handle_tweet;F;[[" status0;[[@Vi;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;F;[;[;I"sStores a tweet. If this tweet is directed at us, create a matching Request. Otherwise, emit a :timeline event.;F;-o;.;/F;0i;1i; @;!I"def handle_tweet(status) return if status.user.screen_name == @username if status.text.match(/@#{@username}/i) Tweet.from(status).make_request else emit(:timeline, Tweet.from(status)) end end;F;"I"def handle_tweet(status);F;#0;$T;LTo; ; ;+; ;;I"'Safubot::Twitter::Bot#reply_header;F;[[" tweet0;[[@Vi;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;0i;1i; @;!I"ndef reply_header(tweet) (["@#{tweet.username}"] + (tweet.header_mentions - ["@#{@username}"])).join end;F;"I"def reply_header(tweet);F;#0;$T;LTo; ; ;+; ;;I" Safubot::Twitter::Bot#reply;F;[[" tweet0[" text0;[[@Vi;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;0i;1i; @;!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;LTo; ; ;+; ;;I"Safubot::Twitter::Bot#pull;F;[;[[@Vi;T;;O;;;[;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;0i;1i; @;!I"def 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: #{e}\n#{e.backtrace.join("\n\t")}" end end;F;"I"def pull( );F;#0;$T;LTo; ; ;+; ;;I"Safubot::Twitter::Bot#run;F;[;[[@Vi;T;;V;;;[;IC;"+Starts our TweetStream client running. ;F;@6;0;F;[;[;I"+Starts our TweetStream client running.;F;-o;.;/F;0i;1i; @;!I"Cdef run Fiber.new do @stream = TweetStream::Client.new(@opts) @stream.on_direct_message do |message| dm = handle_message(message) handle_request(dm.request) end @stream.on_error do |err| # HACK (Mispy): For some reason this is a String instead of an Exception. if err.match(/invalid status code: 401/) Log.error "TweetStream authentication failure!" else Log.error "Unhandled TweetStream error: #{$!} #{$@}" end end begin @stream.userstream do |status| tweet = handle_tweet(status) handle_request(tweet.request) if tweet.is_a? Tweet end rescue Exception => e if e.is_a? Interrupt exit else Log.error "TweetStream event loop exited: #{$!} #{$@}" Log.error "Reinitialising TweetStream in 5 seconds." EM::Timer.new(5) { run } end end Log.info("Twitter client is online at @#{@username} :3") end.resume end;F;"I"def run( );F;#0;$T;LTo; ; ;+; ;;I"%Safubot::Twitter::Bot#initialize;F;[[" options"{};[[@Vi ;T;;Z;;;[;IC;"qOptions are passed straight through to ::TweetStream and ::Twitter, but the :username is ours and important. ;F;@C;0;F;[;[o;& ;'I" return;F;(I"a new instance of Bot;F;0;)[I"Bot;F;@C;I"qOptions are passed straight through to ::TweetStream and ::Twitter, but the :username is ours and important.;F;-o;.;/F;0i;1i; @;!I"ldef initialize(options={}) defaults = { :username => 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;LT;3@;4IC;[;3@;5IC;[o;6 ;70;80;90;;\; @;<@;3@;=IC;>{; IC;>{;?T;+IC;>{;nIC;>{;@@;A0;?T;sIC;>{;@@;A0;?T;HIC;>{;@@;A0;?T;?T;?T;B{;C[;[[@Vi;T;;b;;; ;;[;IC;"VA Twitter::Bot instance provides a Safubot::Bot with Twitter-specific processing. ;F;@;0;F;[;[;I"VA Twitter::Bot instance provides a Safubot::Bot with Twitter-specific processing.;F;-o;.;/F;0i;1i; @;I"Safubot::Twitter::Bot;F;Eo;6 ;70;80;90;;F; @;<0;#0;$T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@Vi0;F;: Twitter;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I"Safubot::Twitter;F;$To; ;IC;[o; ; ; ; ;;I"$Safubot::Test.clean_environment;F;[;[[@i;F;:clean_environment;;;[;IC;" ;F;@~;0;F;[;[;I";F; @|;!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;$T;LTo; ; ; ; ;;I"Safubot::Test.request;F;[[" text0;[[@i#;F;: request;;;[;IC;" ;F;@;0;F;[;[;I";F; @|;!I"ldef request(text) Query.create(:user => KnownUser.by_name('testing'), :text => text).make_request end;F;"I"def request(text);F;$T;LT;3@|;4IC;[;3@|;5IC;[;3@|;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@i;F;: Test;;; ;;[;IC;" ;F;@|;0;F;[;[;I";F; @;I"Safubot::Test;F;$To;*;IC;[o; ; ;+; ;;I" Safubot::Query#make_request;F;[;[[@i4;F;;p;;;[;IC;" ;F;@;0;F;[;[;I";F; @;!I"tdef make_request self.request || Request.create(:user => self.user, :text => self.text, :source => self) end;F;"I"def make_request( );F;$T;LT;3@;4IC;[;3@;5IC;[o;6 ;70;8"MongoMapper::Document;9@;;:; o;6 ;70;80;90;;;; @;<0;<0;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[[@i);F;: Query;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I"Safubot::Query;F;Eo;6 ;70;80;90;;F; @;<0;$T;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;IC;>{;@@ ;A@;?T;?T;+IC;>{;?T;?T;B{;C[;[[@i[@0i[@i[@Ti [@i[@i[@Vi [@i[@i;F;: Safubot;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; @;I" Safubot;F;3@;4IC;[;3@;5IC;[;3@;=IC;>{; IC;>{;?T;+IC;>{;?T;?T;B{;C[;[;F;;;;; ;;[;IC;" ;F;@;0;F;[;[;I";F; 0;I";F;@:Safubot.mode@ :Safubot.mode=@:Safubot::Request@):Safubot::Request#callback@+:Safubot::Request#callback=@9:Safubot::Response@_:Safubot::Bot@u:Safubot::Bot#opts@w:Safubot::Bot#twitter@~:Safubot::Bot#xmpp@:Safubot::Bot#request_error@:!Safubot::Bot#process_request@:Safubot::Bot#dispatch@:Safubot::Bot#pull@:Safubot::Bot#process@:Safubot::Bot#respond@:Safubot::Bot#respond_now@:Safubot::Bot#push@:Safubot::Bot#cycle@:Safubot::Bot#concurrently@:Safubot::Bot#run@5:Safubot::Bot#stop@A: Safubot::Bot#enable_twitter@M:Safubot::Bot#enable_xmpp@\:Safubot::Bot#initialize@k:Safubot::Log@:Safubot::Log.path@:Safubot::Log.path=@": Safubot::Log.method_missing@1:Safubot::KnownUser@Q:Safubot::KnownUser.by_xmpp@S:Safubot::XMPP@:Safubot::XMPP::Message@: Safubot::XMPP::Message.from@:$Safubot::XMPP::Message#username@: Safubot::XMPP::Message#user@:(Safubot::XMPP::Message#make_request@:Safubot::XMPP::Bot@ :Safubot::XMPP::Bot#jid@ :Safubot::XMPP::Bot#client@:Safubot::XMPP::Bot#state@$:Safubot::XMPP::Bot#run@0:Safubot::XMPP::Bot#stop@=:Safubot::XMPP::Bot#send@J:"Safubot::XMPP::Bot#initialize@Y:Safubot::Evented@:Safubot::Evented#bind@:Safubot::Evented#unbind@:Safubot::Evented#on@:Safubot::Evented#once@:Safubot::Evented#emit@:Safubot::VERSION@:"Safubot::KnownUser.by_twitter@b:Safubot::KnownUser#tweets@u:Safubot::Twitter@:Safubot::Twitter::Tweet@:Safubot::Twitter::Tweet.[]@:!Safubot::Twitter::Tweet.from@:!Safubot::Twitter::Tweet#text@:,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@B:)Safubot::Twitter::DirectMessage.from@D:)Safubot::Twitter::DirectMessage#text@W:-Safubot::Twitter::DirectMessage#username@d:)Safubot::Twitter::DirectMessage#user@q:1Safubot::Twitter::DirectMessage#make_request@~:Safubot::Twitter::Bot@:#Safubot::Twitter::Bot#username@:!Safubot::Twitter::Bot#client@:Safubot::Twitter::Bot#opts@: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#run@6:%Safubot::Twitter::Bot#initialize@C:Safubot::KnownUser#merge@:Safubot::KnownUser.by_name@:Safubot::Test@|:$Safubot::Test.clean_environment@~:Safubot::Test.request@:Safubot::Query@: Safubot::Query#make_request@:Safubot::Bot#answer@