skinbaron_api_client 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fa0a22e643ff91bf554b575f82934242e59dd3d4861b8bf44bd363e23ee6d07
4
- data.tar.gz: 38c437e8f44500428073f310d9917cd34cf5f39fcd2a06850e08bedf8564e5b7
3
+ metadata.gz: 3a5bc7a27a2b8ed17712e35dc276d278dac29708f10c00083a1efb67c486816c
4
+ data.tar.gz: 3e0d1e16da1488cdcd09e437e456477c060fb25e2066a49b4a1c273ee8a883ff
5
5
  SHA512:
6
- metadata.gz: 2520d780ce93dc9f2f2b159feb44d10c06501b958a47e3c5ccefdc5eec7599d5a3fb800fff504768c734af74ba52c89407dd6ea3b155f487278d9c6a01c5c9b0
7
- data.tar.gz: 7ebfbf5c2d8a2237ab5ea1b27c24a42faa265be0fbb6025a49b06d09fc04babe4b403e4fbc976fd3fb83bcf2f5c9ca8760f4a24e1f8cf6518e9c71753ff9444e
6
+ metadata.gz: a11aba6be419470ce1c00bb17e69f810251a5acde612e44fbb6b8fee988d57fb39d3caf9b58ac986915833e2e30981f0b01669142bbdb896d4f2048a4c7c4378
7
+ data.tar.gz: f16a2ffa2d93823df50af4cacbe0c47fbbe6dae019c6d37fb823403fe94643f1db893ecb12f19393d43b32c69aa2c96c1b1870fb7c0aeee0a5567c76403dfaf0
data/CHANGELOG.md CHANGED
@@ -2,11 +2,30 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.0] - 2024-12-26
6
+
7
+ - Added support for querying multiple items in one request
8
+ - By setting `pages` to -1, ALL available results are fetched instead of default 50
9
+ - Added search options:
10
+ - `pages`: Number of pages to fetch
11
+ - `request_delay`: Delay in seconds between requests (when multiple pages are requested)
12
+ - `min_price`: Minimum price to filter results by
13
+ - `max_price`: Maximum price to filter results by
14
+ - `min_wear`: Minimum wear to filter results by (0.00 to 1.00)
15
+ - `max_wear`: Maximum wear to filter results by (0.00 to 1.00)
16
+ - `stattrak`: Boolean to filter results by stattrak (Omit -> all, true -> only StatTrak, false -> only non-StatTrak)
17
+ - `souvenir`: Boolean to filter results by souvenir (Omit -> all, true -> only Souvenir, false -> only non-Souvenir)
18
+ - `stackable`: Boolean to filter results by stackable (Omit -> all, true -> only Stackable, false -> only non-Stackable)
19
+ - `tradelocked`: Boolean to filter results by trade-lock (Omit -> all, true -> only Trade-Locked, false -> only non-Trade-Locked)
20
+ - `items_per_page`: Number of items per page (Default is 50 which is also the max)
21
+ - `after_saleid`: ID to start the search after a specific sale
22
+ - Documented debug mode in README
23
+
24
+ ## [0.1.0] - 2024-12-25
25
+
5
26
  - Initial release with basic SkinBaron API functionality:
6
27
  - Implemented endpoints:
7
28
  - `search`: Get CS2 items from the marketplace
8
29
  - Error handling for API responses
9
30
  - Request/Response logging
10
31
  - Support for CS2 (appid: 730)
11
-
12
- ## [0.1.0] - 2024-12-22
data/README.md CHANGED
@@ -16,7 +16,7 @@ Then in your Ruby code:
16
16
  require 'skinbaron_api_client'
17
17
 
18
18
  skinbaron = SkinbaronApiClient::Client.new(api_key: "YOUR_API_KEY")
19
- results = skinbaron.search(item: "AK-47 | Redline")
19
+ results = skinbaron.search(items: "AK-47 | Redline")
20
20
 
21
21
  puts results
22
22
  ```
@@ -73,13 +73,56 @@ skinbaron = SkinbaronApiClient::Client.new(
73
73
  )
74
74
  ```
75
75
 
76
- ### Search Items
76
+ ### Search Items -> `Hash[]`
77
77
 
78
- Search for items on the SkinBaron marketplace:
78
+ Search for items on the SkinBaron marketplace.
79
+ Search will always return an `Array` of listing `Hashes`.
79
80
 
80
81
  ```ruby
81
82
  # Search for a specific item
82
- response = skinbaron.search(item: "AK-47 | Redline")
83
+ response = skinbaron.search(items: "AK-47 | Redline")
84
+
85
+ # Search for multiple items
86
+ response = skinbaron.search(items: ["AK-47 | Redline", "M4A4 | Urban DDPAT"])
87
+ ```
88
+
89
+ #### Search Options
90
+
91
+ All given options are passed to the Skinbaron API as query parameters.
92
+ You can pass any of the following options:
93
+
94
+ Required:
95
+
96
+ - `items`: Either a String or an Array of Strings. Only include the weapon name and skin name (e.g. no Factory New, no StatTrak, no Souvenir, etc.)
97
+
98
+ Optional:
99
+
100
+ - `pages`: Number of pages to search.
101
+ - Use -1 to get ALL results of the search. Otherwise, Skinbaron will only return 50 results per page.
102
+ - This works by making multiple requests to the API and combining the results.
103
+ - To prevent rate limiting, when multiple pages are requested, a default 3-second delay is enforced between requests
104
+ - Includes duplicate checking to ensure reliable pagination
105
+ - Default is 1 page if not specified
106
+
107
+ - `min_price`: Minimum price of the items.
108
+ - `max_price`: Maximum price of the items.
109
+ - `min_wear`: Minimum wear/float value of the items. (0.00 to 1.00)
110
+ - `max_wear`: Maximum wear/float value of the items. (0.00 to 1.00)
111
+ - `stattrak`: Boolean to filter by StatTrak. (Omit -> all, true -> only StatTrak, false -> only non-StatTrak)
112
+ - `souvenir`: Boolean to filter by Souvenir. (Omit -> all, true -> only Souvenir, false -> only non-Souvenir)
113
+ - `stackable`: Boolean to filter by Stackable. (Omit -> all, true -> only Stackable, false -> only non-Stackable)
114
+ - `tradelocked`: Boolean to filter by Trade-Locked. (Omit -> all, true -> only Trade-Locked, false -> only non-Trade-Locked)
115
+ - `items_per_page`: Number of items per page. (Default is 50 which is also the max. Use only if you want to decrease the number of results per page)
116
+ - `after_saleid`: ID to start the search after a specific sale.
117
+ - `request_delay`: Delay in seconds between requests. (Default is 3 seconds)
118
+
119
+ Example Requests:
120
+
121
+ ```ruby
122
+ skinbaron.search(items: "AK-47 | Redline", pages: -1)
123
+ skinbaron.search(items: "AWP | Dragon Lore", pages: -1, min_price: 100, max_price: 1000)
124
+ skinbaron.search(items: "P250 | Night", pages: -1, min_wear: 0.00, max_wear: 0.05)
125
+ skinbaron.search(items: "M4A4 | Urban DDPAT", pages: 1, items_per_page: 10) # will only return 10 listings (the 10 newest i believe. not sure tho)
83
126
  ```
84
127
 
85
128
  ### Error Handling
@@ -104,6 +147,14 @@ Logs can be configured during client initialization using:
104
147
  - `request_log_path` - Specific file for request logs
105
148
  - `error_log_path` - Specific file for error logs
106
149
 
150
+ ### Debugging
151
+
152
+ To output requests and responses to the console, set the `debug` option to `true` in the client initialization:
153
+
154
+ ```ruby
155
+ skinbaron = SkinbaronApiClient::Client.new(api_key: "YOUR_API_KEY", debug: true)
156
+ ```
157
+
107
158
  ## Requirements
108
159
 
109
160
  - Ruby >= 3.0.0
@@ -31,8 +31,9 @@ module SkinbaronApiClient
31
31
  end
32
32
  end
33
33
 
34
- def search(item:)
35
- @search_endpoint.call(item: item)
34
+ def search(items:, **options)
35
+ items = Array(items)
36
+ @search_endpoint.call(items: items, **options) || []
36
37
  end
37
38
 
38
39
  private
@@ -46,7 +47,7 @@ module SkinbaronApiClient
46
47
  end
47
48
 
48
49
  def setup_endpoints
49
- @search_endpoint = Endpoints::Search.new(self)
50
+ @search_endpoint = Endpoints::Search.new(self, @config)
50
51
  end
51
52
 
52
53
  def setup_logger
@@ -1,22 +1,89 @@
1
1
  module SkinbaronApiClient
2
2
  module Endpoints
3
3
  class Search
4
- attr_reader :skinbaron_client
4
+ DEFAULT_REQUEST_DELAY = 3 # seconds
5
+ attr_reader :skinbaron_client, :config, :last_request_time
6
+ attr_accessor :request_delay
5
7
 
6
- def initialize(skinbaron_client)
8
+ def initialize(skinbaron_client, config)
7
9
  @skinbaron_client = skinbaron_client
10
+ @config = config
11
+ @last_request_time = nil
8
12
  end
9
13
 
10
- def call(item:)
11
- body = @skinbaron_client.config.base_body.merge({ "search_item": item })
12
- response = @skinbaron_client.http_client.post(endpoint: "Search", body: body)
14
+ # if pages is < 1, fetch all pages (one page is max 50 listings by default)
15
+ def call(items:, **options)
16
+ base_body = @skinbaron_client.config.base_body
17
+ body = build_request_body(base_body, items, options)
18
+ target_pages = options[:pages] || 1
19
+ fetch_all = target_pages < 1
13
20
 
14
- return nil unless response[:status].success?
21
+ listings = []
22
+ seen_ids = Set.new
23
+ last_saleid = nil
24
+ current_page = 0
15
25
 
16
- JSON.parse(response[:body])
26
+ loop do
27
+ enforce_request_delay(delay_seconds: options[:request_delay] || DEFAULT_REQUEST_DELAY)
28
+ body["after_saleid"] = last_saleid if last_saleid
29
+
30
+ puts "REQUEST IS BEING MADE" if @config.debug
31
+ response = @skinbaron_client.http_client.post(endpoint: "Search", body: body)
32
+ @last_request_time = Time.now
33
+
34
+ response_body = JSON.parse(response[:body])
35
+ current_page_listings = response_body["sales"]
36
+
37
+ break if current_page_listings.empty?
38
+
39
+ # if any of the current page listings id is in seen_ids, raise error
40
+ raise Error, "pagination failed" if current_page_listings.any? { |listing| seen_ids.include?(listing["id"]) }
41
+
42
+ current_page_listings.each do |listing|
43
+ seen_ids.add(listing["id"])
44
+ listings << listing
45
+ end
46
+
47
+ last_saleid = current_page_listings.last["id"]
48
+ current_page += 1
49
+ break if !fetch_all && current_page >= target_pages
50
+ end
51
+
52
+ listings
17
53
  rescue JSON::ParserError => e
18
54
  logger.error("Failed to parse search response", error: e.message)
19
- nil
55
+ raise e
56
+ end
57
+
58
+ private
59
+
60
+ def enforce_request_delay(delay_seconds:)
61
+ return unless @last_request_time
62
+
63
+ elapsed = Time.now - @last_request_time
64
+ remaining_delay = delay_seconds - elapsed
65
+
66
+ return unless remaining_delay.positive?
67
+
68
+ sleep(remaining_delay)
69
+ end
70
+
71
+ def build_request_body(base_body, items, options)
72
+ items_query = items.join(";")
73
+ base_body.merge({
74
+ "search_item" => items_query
75
+ }).tap do |body|
76
+ body["min"] = options[:min_price] if options.key?(:min_price)
77
+ body["max"] = options[:max_price] if options.key?(:max_price)
78
+ body["minwear"] = options[:min_wear] if options.key?(:min_wear)
79
+ body["maxwear"] = options[:max_wear] if options.key?(:max_wear)
80
+ body["stattrak"] = options[:stattrak] if options.key?(:stattrak)
81
+ body["souvenir"] = options[:souvenir] if options.key?(:souvenir)
82
+ body["stackable"] = options[:stackable] if options.key?(:stackable)
83
+ body["tradelocked"] = options[:tradelocked] if options.key?(:tradelocked)
84
+ body["items_per_page"] = options[:items_per_page] if options.key?(:items_per_page)
85
+ body["after_saleid"] = options[:after_saleid] if options.key?(:after_saleid)
86
+ end
20
87
  end
21
88
  end
22
89
  end
@@ -35,7 +35,7 @@ module SkinbaronApiClient
35
35
  type: "RESPONSE",
36
36
  url: url,
37
37
  status: http_response.status.to_s,
38
- body: http_response.body.to_s
38
+ body: http_response
39
39
  })
40
40
 
41
41
  {
@@ -49,10 +49,14 @@ module SkinbaronApiClient
49
49
  @request_logger.info("\n\n#{"=" * 80}")
50
50
  @request_logger.info(JSON.pretty_generate(log_data))
51
51
  when "RESPONSE"
52
+ log_data[:body] = JSON.parse(log_data[:body])
52
53
  @request_logger.info("\n#{"-" * 80}")
53
54
  @request_logger.info(JSON.pretty_generate(log_data))
54
55
  @request_logger.info("#{"=" * 80}\n")
55
56
  end
57
+ rescue JSON::ParserError => e
58
+ @logger.error("LOGGER: failed to parse search response", error: e.message)
59
+ @logger.error("LOGGER: Response: #{JSON.pretty_generate(log_data)}")
56
60
  end
57
61
 
58
62
  LEVELS.each do |level|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SkinbaronApiClient
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/logs/errors.log CHANGED
@@ -1,24 +1 @@
1
- I, [2024-12-25T19:36:09.018283 #74992] INFO -- : {
2
- "timestamp": "2024-12-25 19:36:09 +0100",
3
- "level": "error",
4
- "message": "Authentication failed",
5
- "metadata": {
6
- "body": "{\"wrong or unauthenticated request\"}"
7
- }
8
- }
9
- I, [2024-12-25T19:38:09.427416 #75213] INFO -- : {
10
- "timestamp": "2024-12-25 19:38:09 +0100",
11
- "level": "error",
12
- "message": "Authentication failed",
13
- "metadata": {
14
- "body": "{\"wrong or unauthenticated request\"}"
15
- }
16
- }
17
- I, [2024-12-25T19:40:13.146024 #75656] INFO -- : {
18
- "timestamp": "2024-12-25 19:40:13 +0100",
19
- "level": "error",
20
- "message": "Authentication failed",
21
- "metadata": {
22
- "body": "{\"wrong or unauthenticated request\"}"
23
- }
24
- }
1
+ # Logfile created on 2024-12-26 17:10:31 +0100 by logger.rb/v1.6.4