Skip to content
This repository was archived by the owner on Nov 19, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 15 additions & 22 deletions bin/codes
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
#!/usr/bin/env ruby

$:.push File.expand_path('../../lib', __FILE__)

# require 'codes'
# require 'commander'
# require 'credentials_manager/appfile_config'
require 'codes'
require 'commander'
require 'credentials_manager/appfile_config'
require 'credentials_manager/appfile_config'
require 'fastlane_core'

# HighLine.track_eof = false

puts "This tool is currently not working due to the new iTunes Connect interface".red
raise "Error"

class CodesApplication
include Commander::Methods

Expand All @@ -27,13 +24,13 @@ class CodesApplication
c.description = 'Download [num] new promo codes from iTunes Connect'

c.action do |args, options|
username options
username = username(options)
number_of_codes = count(args)
apple_id = apple_id(options)
app_identifier = bundle_id(options, apple_id)
app_identifier = bundle_id(options)
format = download_format options

Codes::CodesRunner.download(number_of_codes: number_of_codes, app_identifier: app_identifier, apple_id: apple_id, output_file_path: options.output_file, format: format, country: options.country)
Codes::CodesRunner.download(username: username, number_of_codes: number_of_codes, app_identifier: app_identifier, apple_id: apple_id, output_file_path: options.output_file, format: format)
end
end

Expand All @@ -42,11 +39,11 @@ class CodesApplication
c.description = 'Display remaining number of promo codes from iTunes Connect'

c.action do |args, options|
username options
username = username(options)
apple_id = apple_id(options)
app_identifier = bundle_id(options, apple_id)

Codes::CodesRunner.display(app_identifier: app_identifier, apple_id: apple_id, output_file_path: options.output_file, country: options.country)
Codes::CodesRunner.display(username: username, app_identifier: app_identifier, apple_id: apple_id, output_file_path: options.output_file_path)
end
end
default_command :download
Expand Down Expand Up @@ -84,8 +81,7 @@ class CodesApplication
user = options.username
user ||= ENV['CODES_USERNAME']
user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)

CredentialsManager::PasswordManager.shared_manager(user) if user
user = CredentialsManager::AccountManager.user unless user

user
end
Expand All @@ -100,22 +96,19 @@ class CodesApplication
format
end

def bundle_id(options, apple_id)
def bundle_id(options)
app_identifier = options.app_identifier
app_identifier ||= ENV['CODES_APP_IDENTIFIER']
app_identifier ||= CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)
app_identifier ||= (FastlaneCore::ItunesSearchApi.fetch(apple_id)['bundleId'] rescue nil) if apple_id
app_identifier ||= ask('App Identifier (Bundle ID, e.g. com.krausefx.app): ')
app_identifier
app_identifier || ENV['CODES_APP_IDENTIFIER']
end

def apple_id(options)
apple_id = options.apple_id
apple_id ||= ENV['CODES_APP_ID']
apple_id
apple_id || ENV['CODES_APP_ID']
end
end

UI = FastlaneCore::UI

begin
FastlaneCore::UpdateChecker.start_looking_for_update('codes')
CodesApplication.new.run
Expand Down
8 changes: 2 additions & 6 deletions codes.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

spec.add_dependency 'fastlane_core', '>= 0.16.0', '< 1.0.0' # all shared code and dependencies

# Frontend Scripting
spec.add_dependency 'phantomjs', '~> 1.9.8' # dependency for poltergeist
spec.add_dependency 'capybara', '~> 2.4.3' # for controlling iTC
spec.add_dependency 'poltergeist', '~> 1.5.1' # headless Javascript browser for controlling iTC
spec.add_dependency 'spaceship', '>= 0.23.0', '< 1.0.0' # all shared code and dependencies
spec.add_dependency 'fastlane_core', '>= 0.37.0', '< 1.0.0' # all shared code and dependencies

# Development only
spec.add_development_dependency 'bundler'
Expand Down
4 changes: 1 addition & 3 deletions lib/codes.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
require 'codes/version'
require 'codes/dependency_checker'
require 'codes/codes_runner'
require 'codes/itunes_connect'

require 'fastlane_core'
require 'spaceship'

module Codes
Helper = FastlaneCore::Helper

DependencyChecker.check_dependencies
end
7 changes: 0 additions & 7 deletions lib/codes/dependency_checker.rb

This file was deleted.

120 changes: 26 additions & 94 deletions lib/codes/itunes_connect.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,43 @@
require 'fastlane_core/itunes_connect/itunes_connect'

module Codes
class ItunesConnect < FastlaneCore::ItunesConnect
PROMO_URL = 'https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/wa/LCAppPage/viewPromoCodes?adamId=[[app_id]]&platform=[[platform]]'
CODE_URL = 'https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/redeemLandingPage?code=[[code]]'
class ItunesConnect

# rubocop:disable Metrics/AbcSize
def download(args)
number_of_codes = args[:number_of_codes]

code_or_codes = number_of_codes == 1 ? 'code' : 'codes'
Helper.log.info "Downloading #{number_of_codes} promo #{code_or_codes}..."
UI.message "Downloading #{number_of_codes} promo #{code_or_codes}..."

fetch_app_data args

# Use Pathname because it correctly handles the distinction between relative paths vs. absolute paths
output_file_path = Pathname.new(args[:output_file_path]) if args[:output_file_path]
output_file_path ||= Pathname.new(File.join(Dir.getwd, "#{@app_identifier || @app_id}_codes.txt"))
fail 'Insufficient permissions to write to output file'.red if File.exist?(output_file_path) && !File.writable?(output_file_path)
visit PROMO_URL.gsub('[[app_id]]', @app_id.to_s).gsub('[[platform]]', @platform)

begin
text_fields = wait_for_elements('input[type=text]')
rescue
raise "Could not open details page for app #{@app_identifier}. Are you sure you are using the correct apple account and have access to this app?".red
end
fail 'There should only be a single text input field to specify the number of codes'.red unless text_fields.count == 1

text_fields.first.set(number_of_codes.to_s)
click_next

# are there any errors ?
errors = []
begin
errors = wait_for_elements('div[id=LCPurpleSoftwarePageWrapperErrorMessage]')
rescue
end
fail errors.first.text.red unless errors.count == 0

Helper.log.debug 'Accepting the App Store Volume Custom Code Agreement'
wait_for_elements('input[type=checkbox]').first.click
click_next
output_file_path ||= Pathname.new(File.join(Dir.getwd, "#{@app.apple_id}_codes.txt"))
User.error('Insufficient permissions to write to output file') if File.exist?(output_file_path) && !File.writable?(output_file_path)

# the find(:xpath, "..") gets the parent element of the previous expression
download_url = wait_for_elements("div[class='large-blue-rect-button']").first.find(:xpath, '..')['href']
promocodes = @app.live_version.generate_promocodes!(number_of_codes)

codes, request_date = download_codes(download_url)
request_date = Time.at(promocodes.effective_date / 1000)
codes = promocodes.codes

format = args[:format]
codes = download_format(codes, format, request_date, app) if format
if format

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the return of the conditional for variable assignment and comparison.

output = download_format(codes, format, request_date, app)
else
output = codes.join("\n")
end

bytes_written = File.write(output_file_path.to_s, codes, mode: 'a+')
Helper.log.warn 'Could not write your codes to the codes.txt file, but you can still access them from iTunes Connect later' if bytes_written == 0
Helper.log.info "Added generated codes to '#{output_file_path}'".green unless bytes_written == 0
bytes_written = File.write(output_file_path.to_s, output, mode: 'a+')
UI.important 'Could not write your codes to the codes.txt file, but you can still access them from iTunes Connect later' if bytes_written == 0
UI.success "Added generated codes to '#{output_file_path}'" unless bytes_written == 0

Helper.log.info "Your codes (requested #{request_date}) were successfully downloaded:".green
puts codes
UI.success "Your codes (requested #{request_date}) were successfully downloaded:"
puts output
end
# rubocop:enable Metrics/AbcSize

def download_format(codes, format, request_date, app)
format = format.gsub(/%([a-z])/, '%{\\1}') # %c => %{c}

codes = codes.split("\n").map do |code|
lines = codes.map do |code|
format % {
c: code,
b: app['bundleId'],
Expand All @@ -73,11 +48,7 @@ def download_format(codes, format, request_date, app)
u: CODE_URL.gsub('[[code]]', code)
}
end
codes.join("\n") + "\n"
end

def app_platform(app)
app['kind'] == 'mac-software' ? 'osx' : 'ios'
lines.join("\n") + "\n"
end

def display(args)
Expand All @@ -87,64 +58,25 @@ def display(args)

# Use Pathname because it correctly handles the distinction between relative paths vs. absolute paths
output_file_path = Pathname.new(args[:output_file_path]) if args[:output_file_path]
output_file_path ||= Pathname.new(File.join(Dir.getwd, "#{@app_identifier || @app_id}_codes_info.txt"))
output_file_path ||= Pathname.new(File.join(Dir.getwd, "#{@app.apple_id}_codes_info.txt"))
fail 'Insufficient permissions to write to output file'.red if File.exist?(output_file_path) && !File.writable?(output_file_path)
visit PROMO_URL.gsub('[[app_id]]', @app_id.to_s).gsub('[[platform]]', @platform)

begin
text_fields = wait_for_elements('input[type=text]')
rescue
raise "Could not open details page for app #{app_identifier}. Are you sure you are using the correct apple account and have access to this app?".red
end
fail 'There should only be a single text input field to specify the number of codes'.red unless text_fields.count == 1
app_promocodes = @app.promocodes.first

remaining_divs = wait_for_elements('div#codes_0')
fail 'There should only be a single text div containing the number of remaining codes'.red unless remaining_divs.count == 1
remaining = remaining_divs.first.text.split(' ')[0]
remaining = app_promocodes.maximum_number_of_codes - app_promocodes.number_of_codes

bytes_written = File.write(output_file_path.to_s, remaining, mode: 'a+')
Helper.log.warn 'Could not write your codes to the codes_info.txt file, but you can still access them from iTunes Connect later' if bytes_written == 0
Helper.log.info "Added information of quantity of remaining codes to '#{output_file_path}'".green unless bytes_written == 0
UI.important 'Could not write your codes to the codes_info.txt file, but you can still access them from iTunes Connect later' if bytes_written == 0
UI.success "Added information of quantity of remaining codes to '#{output_file_path}'" unless bytes_written == 0

puts remaining
end

def click_next
wait_for_elements('input.continueActionButton').first.click
end

def download_codes(url)
host = Capybara.current_session.current_host
url = URI.join(host, url)
Helper.log.debug "Downloading promo code file from #{url}"

cookie_string = ''
page.driver.cookies.each do |key, cookie|
cookie_string << "#{cookie.name}=#{cookie.value};"
end

page = open(url, 'Cookie' => cookie_string)
request_date = page.metas['content-disposition'][0].gsub(/.*filename=.*_(.*).txt/, '\\1')
codes = page.read

[codes, request_date]
end

private

def fetch_app_data(args)
@country = args[:country]
@app_identifier = args[:app_identifier]

@app_id = args[:apple_id]
@app_id ||= (FastlaneCore::ItunesSearchApi.fetch_by_identifier(@app_identifier, @country)['trackId'] rescue nil)

if @app_id.to_i == 0 || @app_identifier.to_s.length == 0
fail "Could not find app using the following information: #{args}. Maybe the app is not in the store. Pass the Apple ID of the app as well!".red
end

app = FastlaneCore::ItunesSearchApi.fetch(@app_id, @country)
@platform = app_platform app
Spaceship::Tunes.login(args[:username])
@app = Spaceship::Tunes::Application.find(args[:apple_id] || args[:app_identifier] )
end
end
end
64 changes: 0 additions & 64 deletions lib/codes/itunes_connect/itunes_connect.rb

This file was deleted.

Loading