clash_of_clans_api 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +39 -7
  3. data/lib/clash_of_clans_api/api.rb +41 -33
  4. data/lib/clash_of_clans_api/client.rb +27 -14
  5. data/lib/clash_of_clans_api/models/base.rb +35 -29
  6. data/lib/clash_of_clans_api/models/builder_base_league.rb +10 -0
  7. data/lib/clash_of_clans_api/models/capital_league.rb +10 -0
  8. data/lib/clash_of_clans_api/models/chat_language.rb +11 -0
  9. data/lib/clash_of_clans_api/models/clan.rb +34 -22
  10. data/lib/clash_of_clans_api/models/clan_capital.rb +11 -0
  11. data/lib/clash_of_clans_api/models/clan_capital_district.rb +11 -0
  12. data/lib/clash_of_clans_api/models/clan_location.rb +11 -0
  13. data/lib/clash_of_clans_api/models/paginated_response.rb +3 -2
  14. data/lib/clash_of_clans_api/models/pagination_cursors.rb +10 -0
  15. data/lib/clash_of_clans_api/models/pagination_paging.rb +10 -0
  16. data/lib/clash_of_clans_api/models/player.rb +36 -28
  17. data/lib/clash_of_clans_api/models/player_house.rb +10 -0
  18. data/lib/clash_of_clans_api/models/player_house_element.rb +10 -0
  19. data/lib/clash_of_clans_api/models/war.rb +18 -0
  20. data/lib/clash_of_clans_api/models/war_attack.rb +14 -0
  21. data/lib/clash_of_clans_api/models/war_clan.rb +19 -0
  22. data/lib/clash_of_clans_api/models/war_clan_member.rb +16 -0
  23. data/lib/clash_of_clans_api/models/war_league.rb +10 -0
  24. data/lib/clash_of_clans_api/token_api.rb +2 -2
  25. data/lib/clash_of_clans_api/token_client.rb +14 -0
  26. data/lib/clash_of_clans_api/utils.rb +0 -25
  27. data/lib/clash_of_clans_api/version.rb +1 -1
  28. metadata +34 -7
  29. data/lib/clash_of_clans_api/endpoint_methods.rb +0 -67
  30. data/lib/clash_of_clans_api/no_success_error.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17f2ed1ae9f529afa9f24bb6ee50bc3f35b70fb420bd972b22feedcf5df185da
4
- data.tar.gz: 464de1457dd80505922d87511956aa4db139bf42ac51bea7a73d17d9f9572423
3
+ metadata.gz: c028d861e525d6323453207f3f14885ed9df98346013bd5f586b34e79f95bd41
4
+ data.tar.gz: b39deae44556a97575faa02e5902a945bff658ad77367fb8f616045e2b705da7
5
5
  SHA512:
6
- metadata.gz: 249a37b6c0fa133af87ef742a7f23ede23fc85264a898f5c32672ff6be0c1099f00d6471a776eca315e09fbeb2ec7e248d466954bdc0cc1cdb03034bc41f7064
7
- data.tar.gz: da0f8eca3f5482d89f091eb033f28de91d23f42c031ef5420b328c42dc02ae1cf859e744554c66d8695c0dc89f2454010bce1d7843561b3b8ab6a4a6dec017ed
6
+ metadata.gz: fffdf976c06723cc81d1134c58f832e24804729420d33fb3bc7645595ef6c0a54b45bcdfcbc68264ca6dfdaf58b525adde6cf7ac15802cdc30dae67e2ce08f16
7
+ data.tar.gz: 37644654dd7fdaa88b98d93648cd738a0aec546bf59be37e1443a69cd847b7219bf7426d56226aa1b43f19a59689f007b2be9bce3033ccee6b1d845851e0ea21
data/README.adoc CHANGED
@@ -28,12 +28,12 @@ $ gem install clash_of_clans_api
28
28
 
29
29
  ==== API Communication
30
30
 
31
- To communicate with the Clash of Clans API, an API access token is required.
32
- Currently, the gem is not able to create tokens itself.
31
+ To communicate with the Clash of Clans API, an API key is required.
32
+ If you like to use the library to create and manage API keys, see <<Token API>>.
33
33
 
34
34
  The gem provides two classes for communication.
35
35
  `ClashOfClansApi::Api` is a low-level interface that implements methods for all API endpoints.
36
- If the request is successful, the API’s JSON response is parsed and returned, otherwise a `ClashOfClans::NoSucessError` is raised.
36
+ If the request is successful, the API’s JSON response is parsed and returned, otherwise an `ApiFrame::NoSuccessError` is raised.
37
37
  `ClashOfClansApi::Client` is a higher-level interface that exposes its `ClashOfClansApi::Api` instance through `ClashOfClansApi::Client#api`.
38
38
  Both classes’ initializers take a single argument, the API token.
39
39
 
@@ -44,20 +44,52 @@ They are derived from the https://developer.clashofclans.com/#/documentation[API
44
44
  . Replace slashes (`/`) with underscores (`\_`) and keep only inner ones (`clans_{clanTag}_currentwar_leaguegroup`).
45
45
  . In case of path arguments, singularize the path segment referenced by the argument (`clan_{clanTag}_currentwar_leaguegroup`).
46
46
  . Remove path argument segments (`clan_currentwar_leaguegroup`).
47
+ . Remove any hyphens.
47
48
 
48
49
  Path arguments are converted to positional arguments in the order of definition in the original path name.
49
50
  Path arguments will automatically be URL-escaped.
50
51
  A URL query in the form of a `Hash` can be passed as the named parameter `query:`.
51
52
 
53
+ ==== Token API
54
+
55
+ Creating, listing and deleting API keys (tokens) is supported in `ClashOfClansApi::TokenClient` using the email address and password from the Clash of Clans Developer website.
56
+
57
+ [source,ruby]
58
+ ----
59
+ token_client = ClashOfClansApi::TokenClient.new(email, password)
60
+ token_client.login!
61
+
62
+ # Return a list of the currently registered API keys.
63
+ token_client.list_api_keys
64
+
65
+ # Create a new API key for the IP address 127.0.0.1 and create a ClashOfClansApi::Client from it.
66
+ token = token_client.create_api_key('my_key_name', 'This is a description for my API key.', ['127.0.0.1'])
67
+ client = token.client_from_token
68
+
69
+ # Create or get an API key with the given name for the current IPv4 address.
70
+ other_token = token_client.create_or_get_api_key_for_current_ipv4_address('test_key_name', overwrite: true)
71
+
72
+ # Revoke the previously generated tokens.
73
+ token_client.revoke_api_key(token.id)
74
+ other_token.revoke
75
+
76
+ # Logging out is not required, but possible.
77
+ token_client.logout
78
+ ----
79
+
80
+ This feature can be useful for applications whose host changes their IP address without human interaction as well as for large (federated) applications with too many requests for a single or a few manually created tokens.
81
+
82
+ Similar to `ClashOfClansApi::Api` and `ClashOfClansApi::Client`, there is also a lower-level implementation of the token API in `ClashOfClansApi::TokenApi`.
83
+ However, its usage is not recommended.
84
+
52
85
  ==== Clan and player tags
53
86
 
54
87
  Tags in Clash of Clans are subject to format restrictions.
55
88
  Since those restrictions are well known, `ClashOfClansApi::Tags` provides class methods for checking the format (`.sanitizable?`) and sanitizing ill-formatted tags up to a certain degree (`.sanitize`).
56
89
 
57
- Even though the API seems to ignore some mistakes, e.g. using `O` (upper case letter o) instead of `0` (number zero), it also does not seem to correct them.
58
- If a player with tag `#PY0` existed, the API would return the same information for both `#PY0` and `#PYO`, except using the tag that was requested.
59
- This could lead to unforeseen errors like multiple database entries for the same player or clan.
60
- Therefore, those mistakes should be catched before sending a request.
90
+ Since the beginning of 2022, the API consistently corrects the letter `O` in requests to the number `0`, which was not the case before.
91
+ Previously, this could lead to multiple database entries for the same player, which the `.sanitize` method could prevent.
92
+ While this isn’t a problem anymore in this specific use case, it might come in handy for querying user input in ones own database.
61
93
 
62
94
 
63
95
  === Development
@@ -1,10 +1,10 @@
1
- require_relative 'endpoint_methods'
2
- require_relative 'no_success_error'
1
+ require 'api_frame'
2
+
3
3
  require_relative 'utils'
4
4
 
5
5
  module ClashOfClansApi
6
6
  class Api
7
- include EndpointMethods
7
+ include ApiFrame::EndpointMethods
8
8
 
9
9
  BASE_URI = URI('https://api.clashofclans.com/v1/')
10
10
 
@@ -18,40 +18,48 @@ module ClashOfClansApi
18
18
  @api_token = api_token
19
19
  end
20
20
 
21
- def endpoint_headers
21
+ def default_headers
22
22
  {
23
23
  'Authorization' => "Bearer #{api_token}",
24
24
  }
25
25
  end
26
26
 
27
- define_endpoint :clan_currentwar_leaguegroup, method: :get, endpoint: proc { |clan_tag | "clans/#{Utils.url_escape(clan_tag)}/currentwar/leaguegroup" }
28
- define_endpoint :clanwarleagues_war, method: :get, endpoint: proc { | war_tag | "clanwarleagues/wars/#{Utils.url_escape(war_tag)}" }
29
- define_endpoint :clan_warlog, method: :get, endpoint: proc { |clan_tag | "clans/#{Utils.url_escape(clan_tag)}/warlog" }
30
- define_endpoint :clans, method: :get, endpoint: 'clans'
31
- define_endpoint :clan_currentwar, method: :get, endpoint: proc { |clan_tag | "clans/#{Utils.url_escape(clan_tag)}/currentwar" }
32
- define_endpoint :clan, method: :get, endpoint: proc { |clan_tag | "clans/#{Utils.url_escape(clan_tag)}" }
33
- define_endpoint :clan_members, method: :get, endpoint: proc { |clan_tag | "clans/#{Utils.url_escape(clan_tag)}/members" }
34
-
35
- define_endpoint :player, method: :get, endpoint: proc { |player_tag | "players/#{Utils.url_escape(player_tag)}" }
36
- define_endpoint :player_verifytoken, method: :post, endpoint: proc { |player_tag | "players/#{Utils.url_escape(player_tag)}/verifytoken" }, body: proc { |token:| %Q({"token":"#{token}"}) }
37
-
38
- define_endpoint :leagues, method: :get, endpoint: 'leagues'
39
- define_endpoint :league_season, method: :get, endpoint: proc { | league_id, season_id| "leagues/#{Utils.url_escape( league_id)}/seasons/#{Utils.url_escape(season_id)}" }
40
- define_endpoint :league, method: :get, endpoint: proc { | league_id | "leagues/#{Utils.url_escape( league_id)}" }
41
- define_endpoint :league_seasons, method: :get, endpoint: proc { | league_id | "leagues/#{Utils.url_escape( league_id)}/seasons" }
42
- define_endpoint :warleague, method: :get, endpoint: proc { |warleague_id | "warleagues/#{Utils.url_escape(warleague_id)}" }
43
- define_endpoint :warleagues, method: :get, endpoint: 'warleagues'
44
-
45
- define_endpoint :location_rankings_clans, method: :get, endpoint: proc { |location_id | "locations/#{Utils.url_escape(location_id)}/rankings/clans" }
46
- define_endpoint :location_rankings_players, method: :get, endpoint: proc { |location_id | "locations/#{Utils.url_escape(location_id)}/rankings/players" }
47
- define_endpoint :location_rankings_clansversus, method: :get, endpoint: proc { |location_id | "locations/#{Utils.url_escape(location_id)}/rankings/clans-versus" }
48
- define_endpoint :location_rankings_playersversus, method: :get, endpoint: proc { |location_id | "locations/#{Utils.url_escape(location_id)}/rankings/players-versus" }
49
- define_endpoint :locations, method: :get, endpoint: 'locations'
50
- define_endpoint :location, method: :get, endpoint: proc { |location_id | "locations/#{Utils.url_escape(location_id)}" }
51
-
52
- define_endpoint :goldpass_seasons_current, method: :get, endpoint: 'goldpass/seasons/current'
53
-
54
- define_endpoint :labels_players, method: :get, endpoint: 'labels/players'
55
- define_endpoint :labels_clans, method: :get, endpoint: 'labels/clans'
27
+ define_endpoint :clan_currentwar_leaguegroup, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}/currentwar/leaguegroup" }
28
+ define_endpoint :clanwarleagues_war, method: :get, endpoint: proc { | war_tag| "clanwarleagues/wars/#{ApiFrame::Utils.url_escape(war_tag)}" }
29
+ define_endpoint :clan_warlog, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}/warlog" }
30
+ define_endpoint :clans, method: :get, endpoint: 'clans'
31
+ define_endpoint :clan_currentwar, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}/currentwar" }
32
+ define_endpoint :clan, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}" }
33
+ define_endpoint :clan_members, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}/members" }
34
+ define_endpoint :clan_capitalraidseasons, method: :get, endpoint: proc { |clan_tag| "clans/#{ApiFrame::Utils.url_escape(clan_tag)}/capitalraidseasons" }
35
+
36
+ define_endpoint :player, method: :get, endpoint: proc { |player_tag| "players/#{ApiFrame::Utils.url_escape(player_tag)}" }
37
+ define_endpoint :player_verifytoken, method: :post, endpoint: proc { |player_tag| "players/#{ApiFrame::Utils.url_escape(player_tag)}/verifytoken" }, body: proc { |token:| %Q({"token":"#{token}"}) }
38
+
39
+ define_endpoint :capitalleagues, method: :get, endpoint: 'capitalleagues'
40
+ define_endpoint :leagues, method: :get, endpoint: 'leagues'
41
+ define_endpoint :league_season, method: :get, endpoint: proc { | league_id, season_id | "leagues/#{ApiFrame::Utils.url_escape( league_id)}/seasons/#{ApiFrame::Utils.url_escape(season_id)}" }
42
+ define_endpoint :capitalleague, method: :get, endpoint: proc { | capitalleague_id | "capitalleagues/#{ApiFrame::Utils.url_escape( capitalleague_id)}" }
43
+ define_endpoint :builderbaseleague, method: :get, endpoint: proc { |builderbaseleague_id | "builderbaseleagues/#{ApiFrame::Utils.url_escape(builderbaseleague_id)}" }
44
+ define_endpoint :builderbaseleagues, method: :get, endpoint: 'builderbaseleagues'
45
+ define_endpoint :league, method: :get, endpoint: proc { | league_id | "leagues/#{ApiFrame::Utils.url_escape( league_id)}" }
46
+ define_endpoint :league_seasons, method: :get, endpoint: proc { | league_id | "leagues/#{ApiFrame::Utils.url_escape( league_id)}/seasons" }
47
+ define_endpoint :warleague, method: :get, endpoint: proc { | warleague_id | "warleagues/#{ApiFrame::Utils.url_escape( warleague_id)}" }
48
+ define_endpoint :warleagues, method: :get, endpoint: 'warleagues'
49
+
50
+ define_endpoint :location_rankings_clans, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/clans" }
51
+ define_endpoint :location_rankings_players, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/players" }
52
+ define_endpoint :location_rankings_clansversus, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/clans-versus" }
53
+ define_endpoint :location_rankings_playersbuilderbase, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/players-builder-base" }
54
+ define_endpoint :location_rankings_clansbuilderbase, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/clans-builder-base" }
55
+ define_endpoint :location_rankings_playersversus, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/players-versus" }
56
+ define_endpoint :locations, method: :get, endpoint: 'locations'
57
+ define_endpoint :location_rankings_capitals, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}/rankings/capitals" }
58
+ define_endpoint :location, method: :get, endpoint: proc { |location_id| "locations/#{ApiFrame::Utils.url_escape(location_id)}" }
59
+
60
+ define_endpoint :goldpass_seasons_current, method: :get, endpoint: 'goldpass/seasons/current'
61
+
62
+ define_endpoint :labels_players, method: :get, endpoint: 'labels/players'
63
+ define_endpoint :labels_clans, method: :get, endpoint: 'labels/clans'
56
64
  end
57
65
  end
@@ -3,6 +3,7 @@ require_relative 'models/paginated_response'
3
3
  require_relative 'models/clan'
4
4
  require_relative 'models/player'
5
5
  require_relative 'models/league'
6
+ require_relative 'models/war'
6
7
 
7
8
  module ClashOfClansApi
8
9
  class Client
@@ -16,6 +17,26 @@ module ClashOfClansApi
16
17
  api.perform_request(:get, 'test').code == '404'
17
18
  end
18
19
 
20
+ def clan_currentwar(clan_tag)
21
+ Models::War.new(api.clan_currentwar(clan_tag), self)
22
+ end
23
+
24
+ def clan_warlog(clan_tag)
25
+ Models::PaginatedResponse.new(Models::War, api.clan_warlog(clan_tag), self)
26
+ end
27
+
28
+ def clan(tag)
29
+ Models::Clan.new(api.clan(tag), self)
30
+ end
31
+
32
+ def clan_members(tag)
33
+ Models::PaginatedResponse.new(Models::Player, api.clan_members(tag), self)
34
+ end
35
+
36
+ def player(tag)
37
+ Models::Player.new(api.player(tag), self)
38
+ end
39
+
19
40
  def player_verifytoken(player_tag, token)
20
41
  response = api.player_verifytoken(player_tag, token: token)
21
42
 
@@ -32,28 +53,20 @@ module ClashOfClansApi
32
53
  end
33
54
  end
34
55
 
35
- def clan(tag)
36
- Models::Clan.new(api.clan(tag), self)
37
- end
38
-
39
- def clan_members(tag)
40
- Models::PaginatedResponse.new(Models::Player, api.clan_members(tag), self)
56
+ def capitalleagues
57
+ Models::PaginatedResponse.new(Models::CapitalLeague, api.capitalleagues, self)
41
58
  end
42
59
 
43
60
  def leagues
44
61
  Models::PaginatedResponse.new(Models::League, api.leagues, self)
45
62
  end
46
63
 
47
- def league(id)
48
- Models::League.new(api.league(id), self)
49
- end
50
-
51
- def player(tag)
52
- Models::Player.new(api.player(tag), self)
64
+ def league_season(league_id=29000022, season_id, limit: 10, before: nil, after: nil) # rubocop:disable Style/OptionalArguments
65
+ Models::PaginatedResponse.new(Models::Player, api.league_season(league_id, season_id, query: {limit: limit, before: before, after: after}.compact), self)
53
66
  end
54
67
 
55
- def league_season(league_id=29000022, season_id, limit: nil, before: nil, after: nil) # rubocop:disable Style/OptionalArguments
56
- Models::PaginatedResponse.new(Models::Player, api.league_season(league_id, season_id, query: {limit: limit, before: before, after: after}.compact), self)
68
+ def league(id)
69
+ Models::League.new(api.league(id), self)
57
70
  end
58
71
  end
59
72
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'invalid_data_error'
2
+ require 'date'
2
3
 
3
4
  module ClashOfClansApi
4
5
  module Models
@@ -21,11 +22,13 @@ module ClashOfClansApi
21
22
  end
22
23
 
23
24
  class << self
24
- attr_reader :required_fields
25
+ def registered_properties
26
+ @registered_properties ||= {}
27
+ end
25
28
 
26
- def property(name, key, type: nil, required: false, default: nil)
27
- define_method(name) do
28
- type =
29
+ def property(method_name, key, type: nil, required: false, default: nil)
30
+ define_method(method_name) do
31
+ deduced_type =
29
32
  case type
30
33
  when Symbol
31
34
  send(type)
@@ -35,56 +38,59 @@ module ClashOfClansApi
35
38
  type
36
39
  end
37
40
 
38
- if !@hash.key?(key) && !default.nil?
41
+ if !@hash.key?(key)
39
42
  default
40
- elsif type.nil?
43
+ elsif deduced_type.nil?
41
44
  self[key]
42
- elsif property_cached?(name)
43
- property_from_cache(name)
45
+ elsif property_cached?(method_name)
46
+ property_from_cache(method_name)
44
47
  else
45
48
  initializer_proc = proc do |item|
46
- if type.ancestors.include?(ClashOfClansApi::Models::Base)
47
- type.new(item, self.client)
49
+ if deduced_type.ancestors.include?(ClashOfClansApi::Models::Base)
50
+ deduced_type.new(item, self.client)
51
+ elsif deduced_type == DateTime
52
+ DateTime.parse(item)
48
53
  else
49
- type.new(item)
54
+ deduced_type.new(item)
50
55
  end
51
56
  end
52
57
 
53
- cache_property(name, self[key].then do |prop|
58
+ cache_property(method_name, self[key].then do |prop|
54
59
  prop.is_a?(Array) ? prop.map(&initializer_proc) : initializer_proc.call(prop)
55
60
  end)
56
61
  end
57
62
  end
58
63
 
59
- if required
60
- @required_fields = (@required_fields || []) + [key]
61
- end
64
+ registered_properties[key] = {
65
+ method_name: method_name,
66
+ required: required,
67
+ default: default,
68
+ type: type,
69
+ }
62
70
  end
63
71
  end
64
72
 
65
- def property_cached?(name)
66
- @property_cache && @property_cache.key?(name)
73
+ def property_cached?(method_name)
74
+ @property_cache && @property_cache.key?(method_name)
67
75
  end
68
76
 
69
- def cache_property(name, obj)
77
+ def cache_property(method_name, obj)
70
78
  @property_cache ||= {}
71
79
 
72
- @property_cache[name] = obj
80
+ @property_cache[method_name] = obj
73
81
  end
74
82
 
75
- def property_from_cache(name)
76
- @property_cache[name]
83
+ def property_from_cache(method_name)
84
+ @property_cache[method_name]
77
85
  end
78
86
 
79
87
  def validate!
80
- if self.class.required_fields
81
- missing = self.class.required_fields.reject do |required_field|
82
- @hash.key?(required_field)
83
- end
84
-
85
- if missing.any?
86
- raise InvalidDataError, "The following keys are required, but missing from the model data: #{missing.map(&:inspect).join(', ')}"
87
- end
88
+ missing = self.class.registered_properties.reject do |field_name, properties|
89
+ !properties[:required] || @hash.key?(field_name)
90
+ end
91
+
92
+ if missing.any?
93
+ raise InvalidDataError, "The following keys are required, but missing from the model data: #{missing.keys.map(&:inspect).join(', ')}"
88
94
  end
89
95
  end
90
96
  end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class BuilderBaseLeague < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class CapitalLeague < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class ChatLanguage < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ property :language_code, 'languageCode', required: true
9
+ end
10
+ end
11
+ end
@@ -1,32 +1,44 @@
1
1
  require_relative 'base'
2
+ require_relative 'capital_league'
3
+ require_relative 'chat_language'
4
+ require_relative 'clan_capital'
5
+ require_relative 'clan_location'
2
6
  require_relative 'icon_set'
3
7
  require_relative 'label'
8
+ require_relative 'war_league'
4
9
 
5
10
  module ClashOfClansApi
6
11
  module Models
7
12
  class Clan < Base
8
- property :tag, 'tag', required: true
9
- property :name, 'name', required: true
10
- property :clan_level, 'clanLevel', required: true
11
- property :badge_urls, 'badgeUrls', required: true, type: IconSet
12
- property :type, 'type'
13
- property :location, 'location'
14
- property :clan_points, 'clanPoints'
15
- property :clan_versus_points, 'clanVersusPoints'
16
- property :required_trophies, 'requiredTrophies'
17
- property :war_frequency, 'warFrequency'
18
- property :war_win_streak, 'warWinStreak'
19
- property :war_wins, 'warWins'
20
- property :war_ties, 'warTies'
21
- property :war_losses, 'warLosses'
22
- property :is_war_log_public, 'isWarLogPublic'
23
- property :war_league, 'warLeague'
24
- property :members, 'members'
25
- property :member_list, 'memberList', type: 'ClashOfClansApi::Models::Player'
26
- property :labels, 'labels', type: Label
27
- property :chat_language, 'chatLanguage'
28
- property :required_versus_trophies, 'requiredVersusTrophies'
29
- property :required_townhall_level, 'requiredTownhallLevel'
13
+ property :tag, 'tag', required: true
14
+ property :name, 'name', required: true
15
+ property :type, 'type'
16
+ property :description, 'description'
17
+ property :location, 'location', type: ClanLocation
18
+ property :family_friendly?, 'isFamilyFriendly'
19
+ property :badge_urls, 'badgeUrls', type: IconSet
20
+ property :clan_level, 'clanLevel'
21
+ property :clan_points, 'clanPoints'
22
+ property :clan_builder_base_points, 'clanBuilderBasePoints'
23
+ property :clan_versus_points, 'clanVersusPoints'
24
+ property :clan_capital_points, 'clanCapitalPoints'
25
+ property :capital_league, 'capitalLeague', type: CapitalLeague
26
+ property :required_trophies, 'requiredTrophies'
27
+ property :war_frequency, 'warFrequency'
28
+ property :war_win_streak, 'warWinStreak'
29
+ property :war_wins, 'warWins'
30
+ property :war_ties, 'warTies'
31
+ property :war_losses, 'warLosses'
32
+ property :war_log_public?, 'isWarLogPublic'
33
+ property :war_league, 'warLeague', type: WarLeague
34
+ property :members, 'members'
35
+ property :member_list, 'memberList', type: 'ClashOfClansApi::Models::Player'
36
+ property :labels, 'labels', type: Label
37
+ property :required_builder_base_trophies, 'requiredBuilderBaseTrophies'
38
+ property :required_versus_trophies, 'requiredVersusTrophies'
39
+ property :required_townhall_level, 'requiredTownhallLevel'
40
+ property :clan_capital, 'clanCapital', type: ClanCapital
41
+ property :chat_language, 'chatLanguage', type: ChatLanguage
30
42
  end
31
43
  end
32
44
  end
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+ require_relative 'clan_capital_district'
3
+
4
+ module ClashOfClansApi
5
+ module Models
6
+ class ClanCapital < Base
7
+ property :capital_hall_level, 'capitalHallLevel', required: true
8
+ property :districts, 'districts', required: true, type: ClanCapitalDistrict
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class ClanCapitalDistrict < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ property :district_hall_level, 'districtHallLevel', required: true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class ClanLocation < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ property :country?, 'isCountry', required: true
9
+ end
10
+ end
11
+ end
@@ -1,12 +1,13 @@
1
1
  require_relative 'base'
2
+ require_relative 'pagination_paging'
2
3
 
3
4
  module ClashOfClansApi
4
5
  module Models
5
6
  class PaginatedResponse < Base
6
7
  attr_accessor :item_type
7
8
 
8
- property :items, 'items', type: :item_type, required: true
9
- property :paging, 'paging'
9
+ property :items, 'items', required: true, type: :item_type
10
+ property :paging, 'paging', required: true, type: PaginationPaging
10
11
 
11
12
  def initialize(item_type, hash, client)
12
13
  self.item_type = item_type
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class PaginationCursors < Base
6
+ property :after, 'after'
7
+ property :before, 'before'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+ require_relative 'pagination_cursors'
3
+
4
+ module ClashOfClansApi
5
+ module Models
6
+ class PaginationPaging < Base
7
+ property :cursors, 'cursors', required: true, type: PaginationCursors
8
+ end
9
+ end
10
+ end
@@ -1,43 +1,51 @@
1
1
  require_relative 'base'
2
2
  require_relative 'achievement'
3
+ require_relative 'builder_base_league'
3
4
  require_relative 'hero'
4
5
  require_relative 'label'
5
6
  require_relative 'league'
6
7
  require_relative 'legend_statistics'
8
+ require_relative 'player_house'
7
9
  require_relative 'spell'
8
10
  require_relative 'troop'
9
11
 
10
12
  module ClashOfClansApi
11
13
  module Models
12
14
  class Player < Base
13
- property :tag, 'tag', required: true
14
- property :name, 'name', required: true
15
- property :town_hall_level, 'townHallLevel'
16
- property :town_hall_weapon_level, 'townHallWeaponLevel'
17
- property :exp_level, 'expLevel'
18
- property :trophies, 'trophies', required: true
19
- property :best_trophies, 'bestTrophies'
20
- property :war_stars, 'warStars'
21
- property :attack_wins, 'attackWins'
22
- property :defense_wins, 'defenseWins'
23
- property :builder_hall_level, 'builderHallLevel'
24
- property :versus_trophies, 'versusTrophies'
25
- property :best_versus_trophies, 'bestVersusTrophies'
26
- property :versus_battle_wins, 'versusBattleWins'
27
- property :role, 'role'
28
- property :war_preference, 'warPreference'
29
- property :donations, 'donations'
30
- property :donations_received, 'donationsReceived'
31
- property :rank, 'rank'
32
- property :clan, 'clan', type: 'ClashOfClansApi::Models::Clan'
33
- property :league, 'league', type: League
34
- property :legend_statistics, 'legendStatistics', type: LegendStatistics
35
- property :achievements, 'achievements', type: Achievement
36
- property :versus_battle_win_count, 'versusBattleWinCount'
37
- property :labels, 'labels', type: Label
38
- property :troops, 'troops', type: Troop
39
- property :heroes, 'heroes', type: Hero
40
- property :spells, 'spells', type: Spell
15
+ property :tag, 'tag', required: true
16
+ property :name, 'name', required: true
17
+ property :town_hall_level, 'townHallLevel'
18
+ property :town_hall_weapon_level, 'townHallWeaponLevel'
19
+ property :exp_level, 'expLevel'
20
+ property :trophies, 'trophies'
21
+ property :best_trophies, 'bestTrophies'
22
+ property :war_stars, 'warStars'
23
+ property :attack_wins, 'attackWins'
24
+ property :defense_wins, 'defenseWins'
25
+ property :builder_hall_level, 'builderHallLevel'
26
+ property :builder_base_trophies, 'builderBaseTrophies'
27
+ property :versus_trophies, 'versusTrophies'
28
+ property :best_builder_base_trophies, 'bestBuilderBaseTrophies'
29
+ property :best_versus_trophies, 'bestVersusTrophies'
30
+ property :versus_battle_wins, 'versusBattleWins'
31
+ property :role, 'role'
32
+ property :war_preference, 'warPreference'
33
+ property :clan_rank, 'clanRank'
34
+ property :previous_clan_rank, 'previousClanRank'
35
+ property :donations, 'donations'
36
+ property :donations_received, 'donationsReceived'
37
+ property :rank, 'rank'
38
+ property :clan_capital_contributions, 'clanCapitalContributions'
39
+ property :clan, 'clan', type: 'ClashOfClansApi::Models::Clan'
40
+ property :league, 'league', type: League
41
+ property :builder_base_league, 'builderBaseLeague', type: BuilderBaseLeague
42
+ property :legend_statistics, 'legendStatistics', type: LegendStatistics
43
+ property :achievements, 'achievements', type: Achievement
44
+ property :player_house, 'playerHouse', type: PlayerHouse
45
+ property :labels, 'labels', type: Label
46
+ property :troops, 'troops', type: Troop
47
+ property :heroes, 'heroes', type: Hero
48
+ property :spells, 'spells', type: Spell
41
49
  end
42
50
  end
43
51
  end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+ require_relative 'player_house_element'
3
+
4
+ module ClashOfClansApi
5
+ module Models
6
+ class PlayerHouse < Base
7
+ property :elements, 'elements', type: PlayerHouseElement, required: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class PlayerHouseElement < Base
6
+ property :type, 'type', required: true
7
+ property :id, 'id', required: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+ require_relative 'war_clan'
3
+
4
+ module ClashOfClansApi
5
+ module Models
6
+ class War < Base
7
+ property :state, 'state'
8
+ property :result, 'result'
9
+ property :start_time, 'startTime', type: DateTime
10
+ property :end_time, 'endTime', type: DateTime
11
+ property :preparation_start_time, 'preparationStartTime', type: DateTime
12
+ property :team_size, 'teamSize'
13
+ property :attacks_per_member, 'attacksPerMember'
14
+ property :clan, 'clan', required: true, type: WarClan
15
+ property :opponent, 'opponent', required: true, type: WarClan
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class WarAttack < Base
6
+ property :attacker_tag, 'attackerTag', required: true
7
+ property :defender_tag, 'defenderTag', required: true
8
+ property :stars, 'stars', required: true
9
+ property :destruction_percentage, 'destructionPercentage', required: true
10
+ property :order, 'order', required: true
11
+ property :duration, 'duration', required: true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'base'
2
+ require_relative 'icon_set'
3
+ require_relative 'war_clan_member'
4
+
5
+ module ClashOfClansApi
6
+ module Models
7
+ class WarClan < Base
8
+ property :tag, 'tag'
9
+ property :name, 'name'
10
+ property :badge_urls, 'badgeUrls', type: IconSet
11
+ property :clan_level, 'clanLevel'
12
+ property :attacks, 'attacks'
13
+ property :stars, 'stars'
14
+ property :destruction_percentage, 'destructionPercentage'
15
+ property :exp_earned, 'expEarned'
16
+ property :members, 'members', type: WarClanMember
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'base'
2
+ require_relative 'war_attack'
3
+
4
+ module ClashOfClansApi
5
+ module Models
6
+ class WarClanMember < Base
7
+ property :tag, 'tag', required: true
8
+ property :name, 'name', required: true
9
+ property :townhall_level, 'townhallLevel'
10
+ property :map_position, 'mapPosition'
11
+ property :attacks, 'attacks', type: WarAttack
12
+ property :opponent_attacks, 'opponentAttacks'
13
+ property :best_opponent_attack, 'bestOpponentAttack', type: WarAttack
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module ClashOfClansApi
4
+ module Models
5
+ class WarLeague < Base
6
+ property :id, 'id', required: true
7
+ property :name, 'name', required: true
8
+ end
9
+ end
10
+ end
@@ -1,11 +1,11 @@
1
- require_relative 'endpoint_methods'
1
+ require 'api_frame'
2
2
 
3
3
  module ClashOfClansApi
4
4
  module TokenApi
5
5
  BASE_URI = URI('https://developer.clashofclans.com/api/')
6
6
 
7
7
  class << self
8
- include EndpointMethods
8
+ include ApiFrame::EndpointMethods
9
9
 
10
10
  def base_uri
11
11
  BASE_URI
@@ -52,6 +52,20 @@ module ClashOfClansApi
52
52
  true
53
53
  end
54
54
 
55
+ def create_or_get_api_key_for_current_ipv4_address(name, description=name, overwrite: false)
56
+ current_ipv4 = ClashOfClansApi::Utils.current_ipv4_address
57
+ token = list_api_keys.select{ |i| i.name == name }.first
58
+
59
+ if token && token.cidr_ranges.include?(current_ipv4)
60
+ token
61
+ elsif (token && overwrite) || !token
62
+ token&.revoke
63
+ create_api_key(name, description, current_ipv4)
64
+ else
65
+ false
66
+ end
67
+ end
68
+
55
69
  class << self
56
70
  def create!(email, password)
57
71
  new(email, password).login!
@@ -2,31 +2,6 @@ require 'open-uri'
2
2
 
3
3
  module ClashOfClansApi
4
4
  module Utils
5
- def self.url_escape(string)
6
- if !string.nil?
7
- CGI.escape(string.to_s)
8
- else
9
- raise TypeError, 'cannot escape nil'
10
- end
11
- end
12
-
13
- def self.call_proc_without_unknown_keywords(proc, *args, **kwargs, &block)
14
- params = proc.parameters.group_by(&:first).transform_values! do |m|
15
- m.map do |s|
16
- s[1]
17
- end
18
- end
19
-
20
- proc_keys =
21
- if params.key?(:keyrest)
22
- kwargs
23
- else
24
- kwargs.slice(*params.values_at(:key, :keyreq).compact.flatten)
25
- end
26
-
27
- proc.call(*args, **proc_keys, &block)
28
- end
29
-
30
5
  def self.current_ipv4_address
31
6
  IPAddr.new(URI('https://ipv4.icanhazip.com').open.read.strip).to_s
32
7
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClashOfClansApi
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clash_of_clans_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - expeehaa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-13 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-10-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: api_frame
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description:
14
28
  email:
15
29
  - expeehaa@outlook.com
@@ -21,10 +35,15 @@ files:
21
35
  - lib/clash_of_clans_api.rb
22
36
  - lib/clash_of_clans_api/api.rb
23
37
  - lib/clash_of_clans_api/client.rb
24
- - lib/clash_of_clans_api/endpoint_methods.rb
25
38
  - lib/clash_of_clans_api/models/achievement.rb
26
39
  - lib/clash_of_clans_api/models/base.rb
40
+ - lib/clash_of_clans_api/models/builder_base_league.rb
41
+ - lib/clash_of_clans_api/models/capital_league.rb
42
+ - lib/clash_of_clans_api/models/chat_language.rb
27
43
  - lib/clash_of_clans_api/models/clan.rb
44
+ - lib/clash_of_clans_api/models/clan_capital.rb
45
+ - lib/clash_of_clans_api/models/clan_capital_district.rb
46
+ - lib/clash_of_clans_api/models/clan_location.rb
28
47
  - lib/clash_of_clans_api/models/hero.rb
29
48
  - lib/clash_of_clans_api/models/icon_set.rb
30
49
  - lib/clash_of_clans_api/models/invalid_data_error.rb
@@ -33,11 +52,19 @@ files:
33
52
  - lib/clash_of_clans_api/models/legend_season.rb
34
53
  - lib/clash_of_clans_api/models/legend_statistics.rb
35
54
  - lib/clash_of_clans_api/models/paginated_response.rb
55
+ - lib/clash_of_clans_api/models/pagination_cursors.rb
56
+ - lib/clash_of_clans_api/models/pagination_paging.rb
36
57
  - lib/clash_of_clans_api/models/player.rb
58
+ - lib/clash_of_clans_api/models/player_house.rb
59
+ - lib/clash_of_clans_api/models/player_house_element.rb
37
60
  - lib/clash_of_clans_api/models/spell.rb
38
61
  - lib/clash_of_clans_api/models/token.rb
39
62
  - lib/clash_of_clans_api/models/troop.rb
40
- - lib/clash_of_clans_api/no_success_error.rb
63
+ - lib/clash_of_clans_api/models/war.rb
64
+ - lib/clash_of_clans_api/models/war_attack.rb
65
+ - lib/clash_of_clans_api/models/war_clan.rb
66
+ - lib/clash_of_clans_api/models/war_clan_member.rb
67
+ - lib/clash_of_clans_api/models/war_league.rb
41
68
  - lib/clash_of_clans_api/tags.rb
42
69
  - lib/clash_of_clans_api/token_api.rb
43
70
  - lib/clash_of_clans_api/token_client.rb
@@ -56,14 +83,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
83
  requirements:
57
84
  - - ">="
58
85
  - !ruby/object:Gem::Version
59
- version: 2.6.0
86
+ version: 2.7.0
60
87
  required_rubygems_version: !ruby/object:Gem::Requirement
61
88
  requirements:
62
89
  - - ">="
63
90
  - !ruby/object:Gem::Version
64
91
  version: '0'
65
92
  requirements: []
66
- rubygems_version: 3.3.3
93
+ rubygems_version: 3.4.10
67
94
  signing_key:
68
95
  specification_version: 4
69
96
  summary: Client library for interacting with the ClashOfClans API.
@@ -1,67 +0,0 @@
1
- require 'cgi'
2
- require 'json'
3
- require 'net/https'
4
-
5
- module ClashOfClansApi
6
- module EndpointMethods
7
- def endpoint_headers
8
- {}
9
- end
10
-
11
- def perform_request(method, api_path, query: nil, body: nil, headers: nil)
12
- uri = self.base_uri + api_path
13
- uri.query = URI.encode_www_form(query) if query
14
-
15
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
16
- case method
17
- when :get
18
- Net::HTTP::Get
19
- when :post
20
- Net::HTTP::Post
21
- else
22
- raise ArgumentError, "Invalid method #{method.inspect}."
23
- end.new(uri).tap do |request|
24
- endpoint_headers.merge(headers || {}).each do |name, value|
25
- request[name] = value
26
- end
27
-
28
- if body
29
- request['Content-Type'] = 'application/json'
30
- request.body = body
31
- end
32
- end.then do |request|
33
- http.request(request)
34
- end
35
- end
36
- end
37
-
38
- def transform_response(response)
39
- if response.is_a?(Net::HTTPSuccess)
40
- JSON.parse(response.body)
41
- else
42
- raise NoSuccessError, response
43
- end
44
- end
45
-
46
- def self.included(klass)
47
- klass.extend(ClassMethods)
48
- end
49
-
50
- module ClassMethods
51
- def define_endpoint(name, method:, endpoint:, body: nil)
52
- define_method(name) do |*args, **kwargs|
53
- uri = endpoint.respond_to?(:call) ? ClashOfClansApi::Utils.call_proc_without_unknown_keywords(endpoint, *args, **kwargs) : endpoint
54
- request_body = body .respond_to?(:call) ? ClashOfClansApi::Utils.call_proc_without_unknown_keywords(body, *args, **kwargs) : body
55
-
56
- perform_request(method, uri, body: request_body, query: kwargs.key?(:query) ? kwargs.fetch(:query) : nil, headers: kwargs.key?(:headers) ? kwargs.fetch(:headers) : nil).then do |response|
57
- if !kwargs.key?(:plain_response) || !kwargs.fetch(:plain_response)
58
- transform_response(response)
59
- else
60
- response
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,11 +0,0 @@
1
- module ClashOfClansApi
2
- class NoSuccessError < StandardError
3
- attr_reader :response
4
-
5
- def initialize(response)
6
- super
7
-
8
- @response = response
9
- end
10
- end
11
- end