lib/robut-rdio.rb in robut-rdio-0.1.0 vs lib/robut-rdio.rb in robut-rdio-0.1.2

- old
+ new

@@ -43,66 +43,190 @@ def self.start_server @server = Thread.new { Server.run! :host => (host || "localhost"), :port => (port || 4567) } Server.token = self.token || "GAlNi78J_____zlyYWs5ZG02N2pkaHlhcWsyOWJtYjkyN2xvY2FsaG9zdEbwl7EHvbylWSWFWYMZwfc=" Server.domain = self.domain || "localhost" end + + # + # Because an instance of this plugin is not created until the Robut client has + # recieved at least one message. The server callbacks need to be created during + # the #handle request. This allows for the server to communicate back through + # the Robut communication channel that it receives the messages. + # + def establish_server_callbacks! + Server.reply_callback ||= lambda{|message| reply(message, :room)} + Server.state_callback ||= lambda{|message| reply("/me #{message}", :room)} + end # Returns a description of how to use this plugin def usage [ "#{at_nick} play <song> - queues <song> for playing", "#{at_nick} play album <album> - queues <album> for playing", "#{at_nick} play track <track> - queues <track> for playing", "#{at_nick} play/unpause - unpauses the track that is currently playing", "#{at_nick} next - move to the next track", + "#{at_nick} next|skip album - skip all tracks in the current album group", "#{at_nick} restart - restart the current track" ] end + + + # + # @param [String,Array] request that is being evaluated as a play request + # @return [Boolean] + # + def play_results_regex + /^(?:play)?\s?(?:result)?\s?((?:\d[\s,-]*)+|all)$/ + end + + # + # @param [String,Array] request that is being evaluated as a play request + # @return [Boolean] + # + def play?(request) + Array(request).join(' ') =~ play_results_regex + end + # + # @param [Array,String] track_request the play request that is going to be + # parsed for available tracks. + # + # @return [Array] track numbers that were identified. + # + # @example Requesting multiple tracks + # + # "play 1" + # "play 1 2" + # "play 1,2" + # "play 1-3" + # "play 1, 2 4-6" + # "play all" + # + def parse_tracks_to_play(track_request) + if Array(track_request).join(' ') =~ /play all/ + [ 'all' ] + else + Array(track_request).join(' ')[play_results_regex,-1].to_s.split(/(?:\s|,\s?)/).map do |track| + tracks = track.split("-") + (tracks.first.to_i..tracks.last.to_i).to_a + end.flatten + end + end + + # + # @return [Regex] that is used to match searches for their parameters + # @see http://rubular.com/?regex=(find%7Cdo%20you%20have(%5Csany)%3F)%5Cs%3F(.%2B%5B%5E%3F%5D)%5C%3F%3F + # + def search_regex + /(find|do you have(\sany)?)\s?(.+[^?])\??/ + end + + # + # @param [String,Array] request that is being evaluated as a search request + # @return [Boolean] + # + def search?(request) + Array(request).join(' ') =~ search_regex + end + + # + # @param [String,Array] request that is being evaluated as a search and playback + # request + # @return [Boolean] + # + def search_and_play?(request) + Array(request).join(' ') =~ /^play\b[^\b]+/ + end + + # + # @param [String,Array] request that is being evaluated as a command request + # @return [Boolean] + # + def command?(request) + Array(request).join(' ') =~ /^(?:play|(?:un)?pause|next|restart|back|clear)$/ + end + + # + # @param [String,Array] request that is being evaluated as a skip album request + # @return [Boolean] + # + def skip_album?(message) + message =~ /(next|skip) album/ + end + # Queues songs into the Rdio web player. @nick play search query # will queue the first search result matching 'search query' into # the web player. It can be an artist, album, or song. def handle(time, sender_nick, message) - ::Rdio.init(self.class.key, self.class.secret) + + establish_server_callbacks! + words = words(message) if sent_to_me?(message) - if words.join(' ') =~ /^(play)?\s?(result)?\s?\d/ - play_result(words.last.to_i) - elsif words.first == 'play' and words.length > 1 - results = search(words) - result = results.first - if result - queue(result) - else - reply("I couldn't find #{words.join(" ")} on Rdio.") + + if play?(words) + + play_result *parse_tracks_to_play(words) + + elsif search_and_play?(words) + + search_and_play_criteria = words[1..-1].join(" ") + + unless search_and_play_result search_and_play_criteria + reply("I couldn't find '#{search_and_play_criteria}' on Rdio.") end - elsif words.join(' ') =~ /(find|do you have(\sany)?)\s?(.+[^?])\??/ - find(['',Regexp.last_match[-1]]) - else words.first =~ /play|(?:un)?pause|next|restart|back|clear/ - run_command(words.first) + + elsif search?(words) + + find words.join(' ')[search_regex,-1] + + elsif skip_album?(message) + + run_command("next_album") + + else command?(words) + + run_command(words.join("_")) + end end + end + # + # As the plugin is initialized each time a request is made, the plugin maintains + # the state of the results from the last search request to ensure that it will + # be available when someone makes another request. + # def results @@results end - + + private RESULT_DISPLAYER = { - ::Rdio::Album => lambda{|album| "#{album.artist.name} - #{album.name}"}, - ::Rdio::Track => lambda{|track| "#{track.artist.name} - #{track.album.name} - #{track.name}"} + ::Rdio::Album => lambda{|album| "#{album.artist_name} - #{album.name}"}, + ::Rdio::Track => lambda{|track| "#{track.artist_name} - #{track.album_name} - #{track.name}"}, + ::Rdio::Artist => lambda do |artist| + tracks = artist.tracks(nil,0,1) + unless tracks.empty? + "#{artist.name} - #{tracks.first.album_name} - #{tracks.first.name}" + else + "#{artist.name}" + end + end } def run_command(command) Server.command << command end def find(query) - reply("Searching for: #{query[1..-1].join(' ')}...") + reply("Searching for: #{query}...") @@results = search(query) result_display = format_results(@@results) reply(result_display) end @@ -113,21 +237,45 @@ result_display += format_result(result, index) + "\n" end result_display end - def play_result(number) + def play_result(*requests) + if !has_results? reply("I don't have any search results") and return end - - if !has_result?(number) - reply("I don't have that result") and return + + requests = requests.flatten.compact + + # Queue all the songs when the request is 'all' + + if requests.first == "all" + results.length.times {|index| queue result_at(index) } + return end - - queue result_at(number) + + requests.flatten.each do |request| + + if has_result?(request.to_i) + queue result_at(request.to_i) + else + reply("I don't have that result") + end + + end + end + + def search_and_play_result(message) + + if result = Array(search(message)).first + queue(result) + true + end + + end def has_results? @@results && @@results.any? end @@ -151,24 +299,33 @@ response = RESULT_DISPLAYER[search_result.class].call(search_result) puts response "#{index}: #{response}" end - # Searches Rdio for sources matching +words+. If the first word is - # 'track', it only searches tracks, same for 'album'. Otherwise, - # matches both albums and tracks. + # Searches Rdio for sources matching +words+. + # + # Given an array of Strings, which are the search terms, use Rdio to find any + # tracks that match. If the first word happens be `album` then search for + # albums that match the criteria. + # + # def search(words) + ::Rdio.init(self.class.key, self.class.secret) api = ::Rdio::Api.new(self.class.key, self.class.secret) - - if words[1] == "album" - query_string = words[2..-1].join(' ') + words = words.split(' ') + + if words.first == "album" + query_string = words[1..-1].join(' ') results = api.search(query_string, "Album") - elsif words[1] == "track" - query_string = words[2..-1].join(' ') + elsif words.first == "track" + query_string = words[1..-1].join(' ') results = api.search(query_string, "Track") - else + elsif words.first == "artist" query_string = words[1..-1].join(' ') - results = api.search(query_string, "Album,Track") + results = api.search(query_string, "Artist") + else + query_string = words.join(' ') + results = api.search(query_string, "Track") end end end