class Song
Constants
- API_BASE
- DEFAULT_PER_PAGE
- MAX_MATCHES
- QUERY_SPLIT_PATTERN
- STYLE_TITLE
- USER_AGENT
Public Class Methods
Source
# File plugins/song.rb, line 90 def self.auth_query_params(token: nil, key: nil, secret: nil) token = token || ENV["DISCOGS_TOKEN"].to_s.strip key = key || ENV["DISCOGS_KEY"].to_s.strip secret = secret || ENV["DISCOGS_SECRET"].to_s.strip return { token: token } unless token.empty? return { key: key, secret: secret } unless key.empty? || secret.empty? {} end
Source
# File plugins/song.rb, line 100 def self.build_search_url(parsed, per_page: DEFAULT_PER_PAGE, auth: {}) params = { type: "release", per_page: per_page.to_s } if parsed[:artist].to_s.strip.empty? params[:track] = parsed[:track].to_s.strip else params[:artist] = parsed[:artist].to_s.strip params[:track] = parsed[:track].to_s.strip end auth.each { |key, value| params[key] = value if value.to_s.strip.length.positive? } params_query = params.map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&") "#{API_BASE}?#{params_query}" end
Source
# File plugins/song.rb, line 81 def self.display_query(parsed) parts = [] parts << parsed[:artist].to_s.strip unless parsed[:artist].to_s.strip.empty? parts << parsed[:track].to_s.strip unless parsed[:track].to_s.strip.empty? return parsed[:raw].to_s.strip if parts.empty? parts.join(" ยท ") end
Source
# File plugins/song.rb, line 77 def self.display_track_name(track) track.to_s.split(/\s+/).map { |word| word.capitalize }.join(" ") end
Source
# File plugins/song.rb, line 118 def self.extract_artist(result) artist = Array(result["artist"]).map(&:to_s).map(&:strip).reject(&:empty?).first return artist unless artist.to_s.empty? split_release_title(result["title"]).first end
Source
# File plugins/song.rb, line 171 def self.format_match_line(match) "#{match[:artist]} โ #{match[:track]}" end
Source
# File plugins/song.rb, line 175 def self.format_reply(matches, parsed) return no_results_message(parsed) if matches.empty? lines = [IrcFormat.report_header(tag: "SONG", title: display_query(parsed), style: STYLE_TITLE)] lines.concat(matches.map { |match| format_match_line(match) }) lines << IrcFormat.report_footer("#{matches.length} matches", "Discogs") lines.join("\n") end
Source
# File plugins/song.rb, line 135 def self.matches_artist?(artist, query_artist) return true if query_artist.to_s.strip.empty? artist_key = normalize_term(artist) query_key = normalize_term(query_artist) return false if artist_key.empty? || query_key.empty? artist_key.include?(query_key) || query_key.include?(artist_key) end
Source
# File plugins/song.rb, line 32 def self.missing_auth_message "Discogs lookup unavailable โ set DISCOGS_TOKEN or DISCOGS_KEY and DISCOGS_SECRET." end
Source
# File plugins/song.rb, line 36 def self.no_results_message(query) text = display_query(query) header = IrcFormat.report_header(tag: "SONG", title: text, style: STYLE_TITLE) footer = IrcFormat.report_footer("Discogs") [header, "No songs found for #{text}.", footer].join("\n") end
Source
# File plugins/song.rb, line 64 def self.normalize_term(text) text.to_s.downcase.gsub(/[^a-z0-9]/, "") end
Source
# File plugins/song.rb, line 43 def self.parse_query(query) text = query.to_s.strip return { error: true, raw: "" } if text.empty? if text.match?(QUERY_SPLIT_PATTERN) artist, track = text.split(QUERY_SPLIT_PATTERN, 2) artist = artist.to_s.strip track = track.to_s.strip return { error: true, raw: text } if artist.empty? || track.empty? return { artist: artist, track: track, raw: text } end words = text.split(/\s+/) if words.length == 1 { artist: nil, track: words[0], raw: text } else { artist: words[0], track: words[1..].join(" "), raw: text } end end
Source
# File plugins/song.rb, line 145 def self.parse_results(json, parsed:) data = json.is_a?(String) ? JSON.parse(json) : json track_name = display_track_name(parsed[:track] || parsed[:raw]) seen = {} matches = [] Array(data["results"]).each do |result| next unless result["type"].to_s == "release" artist = extract_artist(result) next if artist.to_s.strip.empty? next unless matches_artist?(artist, parsed[:artist]) key = result_key(result, artist, parsed: parsed) next if seen[key] seen[key] = true matches << { artist: artist, track: track_name } break if matches.length >= MAX_MATCHES end matches rescue JSON::ParserError [] end
Source
# File plugins/song.rb, line 125 def self.result_key(result, artist, parsed:) if parsed[:artist].to_s.strip.empty? normalize_term(artist) else _artist_name, release = split_release_title(result["title"]) release_key = normalize_term(release || result["title"]) "#{normalize_term(artist)}|#{release_key}" end end
Source
# File plugins/song.rb, line 184 def self.search(query, fetch:, token: nil, key: nil, secret: nil, per_page: DEFAULT_PER_PAGE) parsed = parse_query(query) return usage_message if parsed[:error] auth = auth_query_params(token: token, key: key, secret: secret) return missing_auth_message if auth.empty? url = build_search_url(parsed, per_page: per_page, auth: auth) json = fetch.call(url) matches = parse_results(json, parsed: parsed) format_reply(matches, parsed) rescue StandardError "Could not search Discogs right now โ try again shortly." end
Source
# File plugins/song.rb, line 68 def self.split_release_title(title) value = title.to_s.strip if (match = value.match(/\A(.+?)\s+-\s+(.+)\z/)) [match[1].strip, match[2].strip] else [value, nil] end end
Source
# File plugins/song.rb, line 28 def self.usage_message "Usage: !song <track> or !song <artist> <track>" end
Public Instance Methods
Source
# File plugins/song.rb, line 207 def execute(m, query) themed_flood_safe_reply(m, search(query)) end
Source
# File plugins/song.rb, line 203 def fetch_json(url) RabbotHttp.fetch(url, user_agent: USER_AGENT) end
Source
# File plugins/song.rb, line 199 def search(query, fetch: method(:fetch_json), token: nil, key: nil, secret: nil) self.class.search(query, fetch: fetch, token: token, key: key, secret: secret) end