sc2cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.gitlab-ci.yml +37 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +13 -0
- data/bin/console +16 -0
- data/bin/setup +18 -0
- data/exe/sc2 +44 -0
- data/lib/sc2cli.rb +7 -0
- data/lib/sc2cli/shared.rb +15 -0
- data/lib/sc2cli/shared/api.rb +79 -0
- data/lib/sc2cli/shared/cache.rb +112 -0
- data/lib/sc2cli/shared/configuration.rb +119 -0
- data/lib/sc2cli/shared/console.rb +69 -0
- data/lib/sc2cli/shared/names.rb +101 -0
- data/lib/sc2cli/shared/region.rb +112 -0
- data/lib/sc2cli/shared/season.rb +123 -0
- data/lib/sc2cli/shared/token.rb +139 -0
- data/lib/sc2cli/subcommands.rb +9 -0
- data/lib/sc2cli/subcommands/history.rb +114 -0
- data/lib/sc2cli/subcommands/history/historymatch.rb +118 -0
- data/lib/sc2cli/subcommands/history/historymatches.rb +72 -0
- data/lib/sc2cli/subcommands/ladder.rb +120 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetails.rb +110 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsmembership.rb +64 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsrank.rb +101 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsteam.rb +114 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsteammember.rb +110 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsteammembers.rb +85 -0
- data/lib/sc2cli/subcommands/ladder/ladderdetailsteams.rb +85 -0
- data/lib/sc2cli/subcommands/ladder/laddersummary.rb +82 -0
- data/lib/sc2cli/subcommands/league.rb +190 -0
- data/lib/sc2cli/subcommands/league/leaguetier.rb +112 -0
- data/lib/sc2cli/subcommands/league/leaguetiers.rb +79 -0
- data/lib/sc2cli/subcommands/raw.rb +89 -0
- data/lib/sc2cli/subcommands/season.rb +74 -0
- data/lib/sc2cli/version.rb +13 -0
- data/sc2cli.gemspec +36 -0
- metadata +85 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
###############################################################################
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
###############################################################################
|
6
|
+
|
7
|
+
module SC2Cli
|
8
|
+
|
9
|
+
###############################################################################
|
10
|
+
|
11
|
+
module Shared
|
12
|
+
|
13
|
+
###############################################################################
|
14
|
+
|
15
|
+
class Console
|
16
|
+
|
17
|
+
###############################################################################
|
18
|
+
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
###############################################################################
|
22
|
+
|
23
|
+
@@colour_warn = 3
|
24
|
+
@@colour_error = 9
|
25
|
+
|
26
|
+
###############################################################################
|
27
|
+
|
28
|
+
def info(message)
|
29
|
+
puts(message)
|
30
|
+
end
|
31
|
+
|
32
|
+
###############################################################################
|
33
|
+
|
34
|
+
def warn(message)
|
35
|
+
puts(format(colour: @@colour_warn, message: message))
|
36
|
+
end
|
37
|
+
|
38
|
+
###############################################################################
|
39
|
+
|
40
|
+
def error(message)
|
41
|
+
puts(format(colour: @@colour_error, message: message))
|
42
|
+
end
|
43
|
+
|
44
|
+
###############################################################################
|
45
|
+
|
46
|
+
def fatal(message)
|
47
|
+
error(message)
|
48
|
+
exit(1)
|
49
|
+
end
|
50
|
+
|
51
|
+
###############################################################################
|
52
|
+
|
53
|
+
def format(colour:, message:)
|
54
|
+
return "\u001b[38;5;" + colour.to_s + "m" + message + "\u001b[0m"
|
55
|
+
end
|
56
|
+
|
57
|
+
###############################################################################
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
###############################################################################
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
###############################################################################
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
###############################################################################
|
@@ -0,0 +1,101 @@
|
|
1
|
+
###############################################################################
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
###############################################################################
|
6
|
+
|
7
|
+
module SC2Cli
|
8
|
+
|
9
|
+
###############################################################################
|
10
|
+
|
11
|
+
module Shared
|
12
|
+
|
13
|
+
###############################################################################
|
14
|
+
|
15
|
+
class Names
|
16
|
+
|
17
|
+
###############################################################################
|
18
|
+
|
19
|
+
@@console = Console.instance
|
20
|
+
|
21
|
+
###############################################################################
|
22
|
+
|
23
|
+
@@file = "names.yaml"
|
24
|
+
|
25
|
+
###############################################################################
|
26
|
+
|
27
|
+
@configuration
|
28
|
+
@path
|
29
|
+
|
30
|
+
###############################################################################
|
31
|
+
|
32
|
+
attr_reader :base
|
33
|
+
attr_reader :id
|
34
|
+
attr_reader :name
|
35
|
+
attr_reader :region
|
36
|
+
|
37
|
+
###############################################################################
|
38
|
+
|
39
|
+
def initialize(configuration:, name:)
|
40
|
+
@configuration = configuration
|
41
|
+
@base = @configuration.base
|
42
|
+
|
43
|
+
@path = File.join(@base, @@file)
|
44
|
+
|
45
|
+
load(name: name)
|
46
|
+
end
|
47
|
+
|
48
|
+
###############################################################################
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
###############################################################################
|
53
|
+
|
54
|
+
def load(name:)
|
55
|
+
id = nil
|
56
|
+
region = nil
|
57
|
+
|
58
|
+
@@console.fatal("Player names file: #{@path} not found!") unless File.file?(@path)
|
59
|
+
|
60
|
+
@@console.info("Reading player names file: #{@path}")
|
61
|
+
yaml = YAML.load(File.read(@path))
|
62
|
+
|
63
|
+
@@console.fatal("Player: #{name} not found in names file!") unless yaml.key?(name)
|
64
|
+
|
65
|
+
player = yaml[name]
|
66
|
+
|
67
|
+
@@console.fatal("Player: #{name} is not stored as a valid hash in the names file!") unless player.kind_of?(Hash)
|
68
|
+
@@console.fatal("Player: #{name} has no ID associated with it!") unless player.key?("id")
|
69
|
+
|
70
|
+
id = player["id"]
|
71
|
+
|
72
|
+
@@console.fatal("Player: #{name} has an ID associated that is not an integer!") unless id.kind_of?(Integer)
|
73
|
+
@@console.fatal("Player: #{name} has an ID associated that is not valid!") unless id > 0
|
74
|
+
|
75
|
+
if player.key?("region") then
|
76
|
+
region = player["region"]
|
77
|
+
|
78
|
+
@@console.fatal("Player: #{name} has a region associated that is not a string!") unless region.kind_of?(String)
|
79
|
+
@@console.fatal("Player: #{name} has a region associated that is blank!") if region.empty?
|
80
|
+
|
81
|
+
region = Region.new(name: region)
|
82
|
+
end
|
83
|
+
|
84
|
+
@id = id
|
85
|
+
@name = name
|
86
|
+
@region = region || @configuration.region
|
87
|
+
end
|
88
|
+
|
89
|
+
###############################################################################
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
###############################################################################
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
###############################################################################
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
###############################################################################
|
@@ -0,0 +1,112 @@
|
|
1
|
+
###############################################################################
|
2
|
+
|
3
|
+
module SC2Cli
|
4
|
+
|
5
|
+
###############################################################################
|
6
|
+
|
7
|
+
module Shared
|
8
|
+
|
9
|
+
###############################################################################
|
10
|
+
|
11
|
+
class Region
|
12
|
+
|
13
|
+
###############################################################################
|
14
|
+
|
15
|
+
@@console = Console.instance
|
16
|
+
|
17
|
+
###############################################################################
|
18
|
+
|
19
|
+
@@name = "eu"
|
20
|
+
|
21
|
+
###############################################################################
|
22
|
+
|
23
|
+
@@regions = {
|
24
|
+
"cn" => {
|
25
|
+
"api_server" => "gateway.battlenet.com.cn",
|
26
|
+
"description" => "China",
|
27
|
+
"id" => 5,
|
28
|
+
"oauth_server" => "www.battlenet.com.cn",
|
29
|
+
"realm" => 1
|
30
|
+
},
|
31
|
+
"eu" => {
|
32
|
+
"api_server" => "eu.api.blizzard.com",
|
33
|
+
"description" => "Europe",
|
34
|
+
"id" => 2,
|
35
|
+
"oauth_server" => "eu.battle.net",
|
36
|
+
"realm" => 1
|
37
|
+
},
|
38
|
+
"la" => {
|
39
|
+
"api_server" => "us.api.blizzard.com",
|
40
|
+
"description" => "Latin America",
|
41
|
+
"id" => 1,
|
42
|
+
"oauth_server" => "us.battle.net",
|
43
|
+
"realm" => 2
|
44
|
+
},
|
45
|
+
"kr" => {
|
46
|
+
"api_server" => "kr.api.blizzard.com",
|
47
|
+
"description" => "Korea",
|
48
|
+
"id" => 3,
|
49
|
+
"oauth_server" => "kr.battle.net",
|
50
|
+
"realm" => 1
|
51
|
+
},
|
52
|
+
"ru" => {
|
53
|
+
"api_server" => "eu.api.blizzard.com",
|
54
|
+
"description" => "Russia",
|
55
|
+
"id" => 2,
|
56
|
+
"oauth_server" => "eu.battle.net",
|
57
|
+
"realm" => 2
|
58
|
+
},
|
59
|
+
"tw" => {
|
60
|
+
"api_server" => "tw.api.blizzard.com",
|
61
|
+
"description" => "Taiwan",
|
62
|
+
"id" => 3,
|
63
|
+
"oauth_server" => "apac.battle.net",
|
64
|
+
"realm" => 2
|
65
|
+
},
|
66
|
+
"us" => {
|
67
|
+
"api_server" => "us.api.blizzard.com",
|
68
|
+
"description" => "United States",
|
69
|
+
"id" => 1,
|
70
|
+
"oauth_server" => "us.battle.net",
|
71
|
+
"realm" => 1
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
###############################################################################
|
76
|
+
|
77
|
+
attr_reader :api_server
|
78
|
+
attr_reader :description
|
79
|
+
attr_reader :id
|
80
|
+
attr_reader :name
|
81
|
+
attr_reader :oauth_server
|
82
|
+
attr_reader :realm
|
83
|
+
|
84
|
+
###############################################################################
|
85
|
+
|
86
|
+
def initialize(name: nil)
|
87
|
+
name = name || @@name
|
88
|
+
|
89
|
+
@@console.fatal("Invalid region: #{name}!") unless @@regions.key?(name)
|
90
|
+
|
91
|
+
@name = name
|
92
|
+
|
93
|
+
@api_server = @@regions[name]["api_server"]
|
94
|
+
@description = @@regions[name]["description"]
|
95
|
+
@id = @@regions[name]["id"]
|
96
|
+
@oauth_server = @@regions[name]["oauth_server"]
|
97
|
+
@realm = @@regions[name]["realm"]
|
98
|
+
end
|
99
|
+
|
100
|
+
###############################################################################
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
###############################################################################
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
###############################################################################
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
###############################################################################
|
@@ -0,0 +1,123 @@
|
|
1
|
+
###############################################################################
|
2
|
+
|
3
|
+
module SC2Cli
|
4
|
+
|
5
|
+
###############################################################################
|
6
|
+
|
7
|
+
module Shared
|
8
|
+
|
9
|
+
###############################################################################
|
10
|
+
|
11
|
+
class Season
|
12
|
+
|
13
|
+
###############################################################################
|
14
|
+
|
15
|
+
@@console = Console.instance
|
16
|
+
|
17
|
+
###############################################################################
|
18
|
+
|
19
|
+
@@prefix = "/sc2/ladder/season"
|
20
|
+
|
21
|
+
###############################################################################
|
22
|
+
|
23
|
+
attr_reader :ends
|
24
|
+
attr_reader :id
|
25
|
+
attr_reader :number
|
26
|
+
attr_reader :region
|
27
|
+
attr_reader :starts
|
28
|
+
attr_reader :year
|
29
|
+
|
30
|
+
###############################################################################
|
31
|
+
|
32
|
+
def initialize(configuration:, region: nil)
|
33
|
+
@region = region || configuration.region
|
34
|
+
|
35
|
+
@@console.info("Finding current season information...")
|
36
|
+
|
37
|
+
path = "#{@@prefix}/#{@region.id.to_s}"
|
38
|
+
|
39
|
+
api = Shared::Api.new(configuration: configuration, region: @region)
|
40
|
+
|
41
|
+
result = api.get(path: path)
|
42
|
+
|
43
|
+
parse(json: result)
|
44
|
+
end
|
45
|
+
|
46
|
+
###############################################################################
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
result = String.new
|
50
|
+
|
51
|
+
result += "-------------------------------------------------------------------------------\n"
|
52
|
+
result += "Season: #{@id.to_s}\n"
|
53
|
+
result += "-------------------------------------------------------------------------------\n"
|
54
|
+
result += "Year : #{@year.to_s}\n"
|
55
|
+
result += "Number: #{@number.to_s}\n"
|
56
|
+
result += "Began : #{@starts.to_s}\n"
|
57
|
+
result += "Ends : #{@ends.to_s}\n\n"
|
58
|
+
|
59
|
+
return result
|
60
|
+
end
|
61
|
+
|
62
|
+
###############################################################################
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
###############################################################################
|
67
|
+
|
68
|
+
def parse(json:)
|
69
|
+
@@console.fatal("Returned season information is missing an end date!") unless json.key?("endDate")
|
70
|
+
@@console.fatal("Returned season information is missing an ID!") unless json.key?("seasonId")
|
71
|
+
@@console.fatal("Returned season information is missing a season number!") unless json.key?("number")
|
72
|
+
@@console.fatal("Returned season information is missing a start date!") unless json.key?("startDate")
|
73
|
+
@@console.fatal("Returned season information is missing a year!") unless json.key?("year")
|
74
|
+
|
75
|
+
ends = json["endDate"]
|
76
|
+
|
77
|
+
ends = ends.to_i if ends.kind_of?(String)
|
78
|
+
|
79
|
+
@@console.fatal("Returned season information has an end date that is not an integer!") unless ends.kind_of?(Integer)
|
80
|
+
@@console.fatal("Returned season information has an end date that is invalid!") unless ends > 0
|
81
|
+
|
82
|
+
id = json["seasonId"]
|
83
|
+
|
84
|
+
@@console.fatal("Returned season information has an ID that is not an integer!") unless id.kind_of?(Integer)
|
85
|
+
@@console.fatal("Returned season information has an ID that is invalid!") unless id >= 0
|
86
|
+
|
87
|
+
number = json["number"]
|
88
|
+
|
89
|
+
@@console.fatal("Returned season information has a number that is not an integer!") unless number.kind_of?(Integer)
|
90
|
+
@@console.fatal("Returned season information has a number that is invalid!") unless number > 0
|
91
|
+
|
92
|
+
starts = json["startDate"]
|
93
|
+
|
94
|
+
starts = starts.to_i if starts.kind_of?(String)
|
95
|
+
|
96
|
+
@@console.fatal("Returned season information has a start date that is not an integer!") unless starts.kind_of?(Integer)
|
97
|
+
@@console.fatal("Returned season information has a start date that is invalid!") unless starts > 0
|
98
|
+
|
99
|
+
year = json["year"]
|
100
|
+
|
101
|
+
@@console.fatal("Returned season information has a year that is not an integer!") unless year.kind_of?(Integer)
|
102
|
+
@@console.fatal("Returned season information has a year that is invalid!") unless year > 2000
|
103
|
+
|
104
|
+
@ends = Time.at(ends)
|
105
|
+
@id = id
|
106
|
+
@number = number
|
107
|
+
@starts = Time.at(starts)
|
108
|
+
@year = year
|
109
|
+
end
|
110
|
+
|
111
|
+
###############################################################################
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
###############################################################################
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
###############################################################################
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
###############################################################################
|
@@ -0,0 +1,139 @@
|
|
1
|
+
###############################################################################
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
###############################################################################
|
7
|
+
|
8
|
+
module SC2Cli
|
9
|
+
|
10
|
+
###############################################################################
|
11
|
+
|
12
|
+
module Shared
|
13
|
+
|
14
|
+
###############################################################################
|
15
|
+
|
16
|
+
class Token
|
17
|
+
|
18
|
+
###############################################################################
|
19
|
+
|
20
|
+
@@console = Console.instance
|
21
|
+
|
22
|
+
###############################################################################
|
23
|
+
|
24
|
+
@@cutoff = 3600
|
25
|
+
|
26
|
+
###############################################################################
|
27
|
+
|
28
|
+
@cache
|
29
|
+
@configuration
|
30
|
+
|
31
|
+
###############################################################################
|
32
|
+
|
33
|
+
attr_reader :expires
|
34
|
+
attr_reader :region
|
35
|
+
attr_reader :token
|
36
|
+
|
37
|
+
###############################################################################
|
38
|
+
|
39
|
+
def initialize(configuration:, region: nil)
|
40
|
+
@configuration = configuration
|
41
|
+
@region = region || @configuration.region
|
42
|
+
|
43
|
+
@@console.info("Finding token for region: #{@region.name} (#{@region.description})")
|
44
|
+
|
45
|
+
@cache = Cache.new(configuration: @configuration, region: @region)
|
46
|
+
|
47
|
+
begin
|
48
|
+
from_cache
|
49
|
+
rescue
|
50
|
+
refresh
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
###############################################################################
|
55
|
+
|
56
|
+
def check
|
57
|
+
return valid(expires: @expires)
|
58
|
+
end
|
59
|
+
|
60
|
+
###############################################################################
|
61
|
+
|
62
|
+
def refresh
|
63
|
+
from_server
|
64
|
+
@cache.update(token: @token, expires: @expires)
|
65
|
+
end
|
66
|
+
|
67
|
+
###############################################################################
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
###############################################################################
|
72
|
+
|
73
|
+
def from_cache
|
74
|
+
raise "Cache 'token' or 'expires' objects are not set!" if @cache.token.nil? or @cache.expires.nil?
|
75
|
+
raise "Cached token has already expired!" unless valid(expires: @cache.expires)
|
76
|
+
|
77
|
+
@token = @cache.token
|
78
|
+
@expires = @cache.expires
|
79
|
+
end
|
80
|
+
|
81
|
+
###############################################################################
|
82
|
+
|
83
|
+
def from_server
|
84
|
+
@@console.fatal("No client/secret in configuration and no usable token in cache!") unless @configuration.auth
|
85
|
+
|
86
|
+
server = @region.oauth_server
|
87
|
+
@@console.info("Refreshing token from: #{server}")
|
88
|
+
|
89
|
+
uri = URI("https://#{server}/oauth/token")
|
90
|
+
request = Net::HTTP::Post.new(uri)
|
91
|
+
|
92
|
+
request.basic_auth(@configuration.client, @configuration.secret)
|
93
|
+
request.set_form_data("grant_type" => "client_credentials")
|
94
|
+
|
95
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
96
|
+
http.use_ssl = true
|
97
|
+
|
98
|
+
response = http.request(request)
|
99
|
+
|
100
|
+
@@console.fatal("Blizzard OAuth did not return status 200 for token reqest!") if response.code != "200"
|
101
|
+
@@console.fatal("Response body from Blizzard OAuth is not permitted!") if not response.class.body_permitted?
|
102
|
+
@@console.fatal("Response body from Blizzard OAuth is empty!") if response.body.nil?
|
103
|
+
|
104
|
+
body = JSON.parse(response.body)
|
105
|
+
|
106
|
+
@@console.fatal("Blizzard OAuth JSON response did not include an access token!") if not body.key?("access_token")
|
107
|
+
@@console.fatal("Blizzard OAuth gave a blank access token!") if body["access_token"].empty?
|
108
|
+
|
109
|
+
@@console.fatal("Blizzard OAuth JSON response did not include an expiry time!") if not body.key?("expires_in")
|
110
|
+
|
111
|
+
token = body["access_token"]
|
112
|
+
expires_in = body["expires_in"]
|
113
|
+
expires = Time.now + expires_in
|
114
|
+
|
115
|
+
@@console.fatal("Token received from Blizzard appears to have already expired!") unless valid(expires: expires)
|
116
|
+
|
117
|
+
@token = token
|
118
|
+
@expires = expires
|
119
|
+
end
|
120
|
+
|
121
|
+
###############################################################################
|
122
|
+
|
123
|
+
def valid(expires:, cutoff: @@cutoff)
|
124
|
+
return (expires - @@cutoff) > Time.now
|
125
|
+
end
|
126
|
+
|
127
|
+
###############################################################################
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
###############################################################################
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
###############################################################################
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
###############################################################################
|