class GymFinder::Client

Public Class Methods

new( username: ENV['GYM_FINDER_USERNAME'], password: ENV['GYM_FINDER_PASSWORD'], retry_captcha: 30 ) click to toggle source
# File lib/gym_finder/client.rb, line 73
def initialize(
  username: ENV['GYM_FINDER_USERNAME'],
  password: ENV['GYM_FINDER_PASSWORD'],
  retry_captcha: 30
)
  @parser = Parser.new
  @username = username
  @password = password
  @retry_captcha = retry_captcha
end

Public Instance Methods

fetch(**params) click to toggle source
# File lib/gym_finder/client.rb, line 84
def fetch(**params)
  captcha_error_count = 0
  begin
    _fetch(**params)
  rescue CaptchaError => error
    captcha_error_count += 1
    raise error if captcha_error_count == @retry_captcha
    retry
  end
end

Private Instance Methods

_fetch( gym_filter: ->(_gym) { true } click to toggle source
# File lib/gym_finder/client.rb, line 97
def _fetch(
  gym_filter: ->(_gym) { true },
  court_filter: ->(_court) { true },
  date_filter: ->(_date) { true }
)
  captcha_error_count = 0
  result = []
  EM.run do
    conn = Conn.new
    conn.done { EM.stop }
    conn.get(path: '/NewCaptcha.aspx') do |client|
      ocr = Ocr.new
      captcha_text = ocr.resolve(client.response)
      conn.cookie = client.response_header['SET_COOKIE'][/ASP\.NET_SessionId=\w+/]
      conn.post(
        path: '/tp03.aspx',
        query: 'module=login_page&files=login',
        body: {
          loginid: @username,
          loginpw: @password,
          Captcha_text: captcha_text
        }
      ) do |client|
        raise CaptchaError unless client.response.include?('修改會員資料')
        GYMS.select(&gym_filter).each do |gym|
          uri = URI(gym.reservation)
          conn.get(path: uri.path) do |client|
            @parser.parse_reservation(client.response).available_courts.select(&court_filter).each do |court|
              query = "module=net_booking&files=booking_place&PT=#{court.pt}"
              conn.get(path: client.req.path, query: query) do |client_calendar|
                @parser.parse_calendar(client_calendar.response).available_dates.select(&date_filter).each do |date|
                  1.upto(3).each do |i|
                    query = "module=net_booking&files=booking_place&StepFlag=2&PT=#{court.pt}&D=#{date.strftime('%Y/%m/%d')}&D2=#{i}"
                    conn.get(path: client_calendar.req.path, query: query) do |client_time_table|
                      result.concat(
                        @parser
                          .parse_time_table(client_time_table.response)
                          .time_slots
                          .map do |time_slot|
                          Slot.new(
                            gym: gym,
                            court: court,
                            date: date,
                            time_slot: time_slot,
                            client: client_time_table
                          )
                        end
                      )
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
  end
  result
end