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 +4 -4
- data/CHANGELOG.md +21 -2
- data/README.md +55 -4
- data/lib/skinbaron_api_client/client.rb +4 -3
- data/lib/skinbaron_api_client/endpoints/search.rb +75 -8
- data/lib/skinbaron_api_client/http_client.rb +1 -1
- data/lib/skinbaron_api_client/logger.rb +4 -0
- data/lib/skinbaron_api_client/version.rb +1 -1
- data/logs/errors.log +1 -24
- data/logs/requests.log +11296 -53
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a5bc7a27a2b8ed17712e35dc276d278dac29708f10c00083a1efb67c486816c
|
4
|
+
data.tar.gz: 3e0d1e16da1488cdcd09e437e456477c060fb25e2066a49b4a1c273ee8a883ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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(
|
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(
|
35
|
-
|
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
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
21
|
+
listings = []
|
22
|
+
seen_ids = Set.new
|
23
|
+
last_saleid = nil
|
24
|
+
current_page = 0
|
15
25
|
|
16
|
-
|
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
|
-
|
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
|
@@ -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|
|
data/logs/errors.log
CHANGED
@@ -1,24 +1 @@
|
|
1
|
-
|
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
|