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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.gitlab-ci.yml +37 -0
  4. data/Gemfile +13 -0
  5. data/LICENSE.txt +21 -0
  6. data/Rakefile +13 -0
  7. data/bin/console +16 -0
  8. data/bin/setup +18 -0
  9. data/exe/sc2 +44 -0
  10. data/lib/sc2cli.rb +7 -0
  11. data/lib/sc2cli/shared.rb +15 -0
  12. data/lib/sc2cli/shared/api.rb +79 -0
  13. data/lib/sc2cli/shared/cache.rb +112 -0
  14. data/lib/sc2cli/shared/configuration.rb +119 -0
  15. data/lib/sc2cli/shared/console.rb +69 -0
  16. data/lib/sc2cli/shared/names.rb +101 -0
  17. data/lib/sc2cli/shared/region.rb +112 -0
  18. data/lib/sc2cli/shared/season.rb +123 -0
  19. data/lib/sc2cli/shared/token.rb +139 -0
  20. data/lib/sc2cli/subcommands.rb +9 -0
  21. data/lib/sc2cli/subcommands/history.rb +114 -0
  22. data/lib/sc2cli/subcommands/history/historymatch.rb +118 -0
  23. data/lib/sc2cli/subcommands/history/historymatches.rb +72 -0
  24. data/lib/sc2cli/subcommands/ladder.rb +120 -0
  25. data/lib/sc2cli/subcommands/ladder/ladderdetails.rb +110 -0
  26. data/lib/sc2cli/subcommands/ladder/ladderdetailsmembership.rb +64 -0
  27. data/lib/sc2cli/subcommands/ladder/ladderdetailsrank.rb +101 -0
  28. data/lib/sc2cli/subcommands/ladder/ladderdetailsteam.rb +114 -0
  29. data/lib/sc2cli/subcommands/ladder/ladderdetailsteammember.rb +110 -0
  30. data/lib/sc2cli/subcommands/ladder/ladderdetailsteammembers.rb +85 -0
  31. data/lib/sc2cli/subcommands/ladder/ladderdetailsteams.rb +85 -0
  32. data/lib/sc2cli/subcommands/ladder/laddersummary.rb +82 -0
  33. data/lib/sc2cli/subcommands/league.rb +190 -0
  34. data/lib/sc2cli/subcommands/league/leaguetier.rb +112 -0
  35. data/lib/sc2cli/subcommands/league/leaguetiers.rb +79 -0
  36. data/lib/sc2cli/subcommands/raw.rb +89 -0
  37. data/lib/sc2cli/subcommands/season.rb +74 -0
  38. data/lib/sc2cli/version.rb +13 -0
  39. data/sc2cli.gemspec +36 -0
  40. 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
+ ###############################################################################