Created
April 10, 2017 11:55
-
-
Save petertseng/04edd07d4b35cda2cbc1d8142b6fcc13 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'json' | |
| require 'time' | |
| # Create a personal access token at https://github.com/settings/tokens | |
| # It needs NO scopes. | |
| # In fact, you could just not use a token, but then you get rate-limited to 60 an hour instead of 5000 an hour. | |
| TOKEN_FILE = 'secrets/token.txt'.freeze | |
| SLEEP_TIME = 2 | |
| CACHE = 'cache'.freeze | |
| Dir.mkdir(CACHE) unless File.exist?(CACHE) | |
| LAST_REQUEST = [0] | |
| def req(url) | |
| cache = "#{CACHE}/#{url.gsub(/\W/, ?_)}" | |
| if File.exist?(cache) | |
| res = File.read(cache) | |
| else | |
| if Time.now.to_i - LAST_REQUEST.first < SLEEP_TIME | |
| puts "#{url}: sleeping to not overload." | |
| Kernel.sleep(SLEEP_TIME) | |
| end | |
| LAST_REQUEST[0] = Time.now.to_i | |
| tok = File.read(TOKEN_FILE).chomp | |
| res = `curl -H "Authorization: token #{tok}" "#{url}"` | |
| File.open(cache, ?w) { |f| f.puts(res) } | |
| end | |
| JSON.parse(res) | |
| rescue => e | |
| puts res | |
| puts "No parse: #{e}" | |
| end | |
| REPO = ARGV.first | |
| case REPO | |
| when 'xgo' | |
| COMMENT_PAGES = 14 | |
| EVENT_PAGES = 31 | |
| ISSUE_PAGES = 7 | |
| when 'xrust' | |
| COMMENT_PAGES = 11 | |
| EVENT_PAGES = 15 | |
| ISSUE_PAGES = 3 | |
| when 'xhaskell' | |
| COMMENT_PAGES = 12 | |
| EVENT_PAGES = 27 | |
| ISSUE_PAGES = 6 | |
| else raise "Unknown repo #{REPO}" | |
| end | |
| comments = (1..COMMENT_PAGES).flat_map { |page| | |
| req("https://api.github.com/repos/exercism/#{REPO}/issues/comments?per_page=100&page=#{page}") | |
| } | |
| events = (1..EVENT_PAGES).flat_map { |page| | |
| req("https://api.github.com/repos/exercism/#{REPO}/issues/events?per_page=100&page=#{page}").select { |i| %w(closed reopened).include?(i['event']) } | |
| } | |
| issues = (1..ISSUE_PAGES).flat_map { |page| | |
| req("https://api.github.com/repos/exercism/#{REPO}/issues?per_page=100&state=all&page=#{page}").reject { |i| i['pull_request'] } | |
| }.map { |i| [i['url'], i] }.to_h | |
| issues.each_value { |i| | |
| i[:events] = [ | |
| # Probot uses 'updated:' search, and this *DOES* take updated_at into account. | |
| # Unfortunately, we only get the most recent update. | |
| # So for issues that were edited after they were closed, we may have false positives. | |
| # (we think it's stale, when it's was not at the time due to an update) | |
| [:fresh, Time.parse(i['updated_at'])], | |
| ] | |
| } | |
| comments.each { |c| | |
| next unless (i = issues[c['issue_url']]) | |
| i[:events] << [:fresh, Time.parse(c['created_at'])] | |
| # Probot uses 'updated:' search, and this *DOES* take updated_at into account. | |
| # Same caveat as above with only getting the most recent update. | |
| i[:events] << [:fresh, Time.parse(c['updated_at'])] | |
| } | |
| events.each { |e| | |
| next unless (i = issues[e['issue']['url']]) | |
| event = case e['event'] | |
| when 'closed'; :close | |
| when 'reopened'; :open | |
| else raise "unknown event #{e}" | |
| end | |
| i[:events] << [event, Time.parse(e['created_at'])] | |
| } | |
| DAY = 86400 | |
| STALE = 60 * DAY | |
| issues.each_value { |i| | |
| open = true | |
| time = Time.parse(i['created_at']) | |
| stale = false | |
| check = ->(etime) { stale ||= open && etime - time >= STALE } | |
| i[:events].sort_by(&:last).each { |event, etime| | |
| case event | |
| when :fresh; check[etime] | |
| when :close; check[etime]; open = false | |
| when :open; open = true | |
| else raise "Unknown event #{event}" | |
| end | |
| time = etime | |
| } | |
| check[Time.now] | |
| next unless stale | |
| puts "%3d %s" % i.values_at('number', 'title') | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment