auciel 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/lib/auciel.rb +51 -0
- data/lib/constants/constants.rb +23 -0
- data/lib/endpoints/hot.rb +44 -0
- data/lib/endpoints/item.rb +87 -0
- data/lib/endpoints/prices.rb +62 -0
- data/lib/endpoints/search.rb +57 -0
- data/lib/endpoints/waitlist.rb +44 -0
- data/lib/utils/api_errors.rb +8 -0
- data/lib/utils/response_formatter.rb +122 -0
- data/readme.md +319 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4a4db1df8b90eb61efa04eb689e247d0ba58d7e2469e0b06b4ccd3dce458511e
|
4
|
+
data.tar.gz: e4ae843ec6cc04501c3c517ae8c25bcf2f715dde4b54d74ea370dfed45a73b11
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 45e2c98006106e4f76778456567dc8a68f7b000042e9afa6d1a5d3e79af7ddff46c5c4736c30dcb6a947c1e0091fb5c69eb6cae878766a23f45c7937342727ff
|
7
|
+
data.tar.gz: 0ac7d1a7ddf033ffa46cfb16d94a3b6ea4c979a074d0004ff32d564032bfd8ddcd244b8cd8360215e3183fc37ff972afcc41fecd2fd07b939b43d2da1323e661
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 jpegzilla
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/lib/auciel.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tsurezure'
|
4
|
+
require_relative './endpoints/item'
|
5
|
+
require_relative './endpoints/search'
|
6
|
+
require_relative './endpoints/prices'
|
7
|
+
require_relative './endpoints/hot'
|
8
|
+
require_relative './endpoints/waitlist'
|
9
|
+
|
10
|
+
# main class for auciel
|
11
|
+
class Auciel
|
12
|
+
include ItemEndpoints
|
13
|
+
include SearchEndpoints
|
14
|
+
include PriceEndpoints
|
15
|
+
include HotEndpoints
|
16
|
+
include WaitlistEndpoints
|
17
|
+
|
18
|
+
def initialize(port = 8888)
|
19
|
+
@server = Tsurezure.new port
|
20
|
+
register_endpoints
|
21
|
+
end
|
22
|
+
|
23
|
+
def register_endpoints
|
24
|
+
# /item/:id
|
25
|
+
@server.register(*ItemEndpoints::GET_ITEM_BY_ID)
|
26
|
+
|
27
|
+
# /item/:cat/:sub
|
28
|
+
@server.register(*ItemEndpoints::GET_ITEM_BY_CATEGORY)
|
29
|
+
|
30
|
+
# /search
|
31
|
+
# requires a urlencoded string
|
32
|
+
@server.register(*SearchEndpoints::SEARCH_ITEM_BY_TEXT)
|
33
|
+
|
34
|
+
# /prices/:id
|
35
|
+
@server.register(*PriceEndpoints::GET_PRICE_LIST_BY_ID_AND_NAME)
|
36
|
+
|
37
|
+
# /hot
|
38
|
+
@server.register(*HotEndpoints::GET_HOT_ITEMS_LIST)
|
39
|
+
|
40
|
+
# /waitlist
|
41
|
+
@server.register(*WaitlistEndpoints::GET_WAITLIST_ITEMS)
|
42
|
+
end
|
43
|
+
|
44
|
+
def listen
|
45
|
+
@server.listen lambda { |opts|
|
46
|
+
puts "listening on port #{opts[:port]}!"
|
47
|
+
}
|
48
|
+
rescue Interrupt
|
49
|
+
puts '[auciel] shutting down.'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Constants
|
4
|
+
COOKIE =
|
5
|
+
'__RequestVerificationToken=0q_pyZ8OMkALLxxv_pj6ty10hXKTDq3Sl_1NAbV5WYhUAbNvcurT6GwSUDmBRnaZVyEmd0lcTlR3I6X7_cXkAwH-nGQG_G2E6MQZPNcfNd41'
|
6
|
+
RVT = 'MGf54RSTVQkYrPo4Dvaf9IOuSilYwQIzWfdBI4PhFrV1l1o_e0BRmf78J6I7pVn_gcqj5DAv-G3MfFiWoUqFE3R62-Kxdcm4CgkRGd7oq7Y1'
|
7
|
+
ROOT_URL = 'https://na-trade.naeu.playblackdesert.com/Home'
|
8
|
+
WORLD_MARKET_LIST = '/GetWorldMarketList'
|
9
|
+
MARKET_SUB_LIST = '/GetWorldMarketSubList'
|
10
|
+
MARKET_SEARCH_LIST = '/GetWorldMarketSearchList'
|
11
|
+
MARKET_SELL_BUY_INFO = '/GetItemSellBuyInfo'
|
12
|
+
MARKET_HOT_LIST = '/GetWorldMarketHotList'
|
13
|
+
MARKET_WAIT_LIST = '/GetWorldMarketWaitList'
|
14
|
+
REQUEST_OPTS = {
|
15
|
+
method: 'post',
|
16
|
+
headers: {
|
17
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
18
|
+
'User-Agent':
|
19
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
|
20
|
+
Cookie: COOKIE
|
21
|
+
}
|
22
|
+
}.freeze
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require_relative './../constants/constants'
|
5
|
+
require_relative './../utils/response_formatter'
|
6
|
+
|
7
|
+
# get a list of currently popular items
|
8
|
+
module HotEndpoints
|
9
|
+
include Constants
|
10
|
+
include FormData
|
11
|
+
include ResponseFormatter
|
12
|
+
|
13
|
+
def self.send_post_request(uri)
|
14
|
+
HTTParty.post(
|
15
|
+
uri,
|
16
|
+
headers: Constants::REQUEST_OPTS[:headers]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.respond_if_available(res)
|
21
|
+
return res.body if res.body
|
22
|
+
|
23
|
+
{
|
24
|
+
error: 'invalid response from the black desert api.',
|
25
|
+
response: res
|
26
|
+
}.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.hot_items
|
30
|
+
lambda do |_req|
|
31
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::MARKET_HOT_LIST}"
|
32
|
+
res = send_post_request uri
|
33
|
+
|
34
|
+
ResponseFormatter.format_hot_response(respond_if_available(res))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
GET_HOT_ITEMS_LIST = {
|
39
|
+
method: 'get',
|
40
|
+
path: '/hot',
|
41
|
+
responder: hot_items,
|
42
|
+
opts: { content_type: 'application/json' }
|
43
|
+
}.values.freeze
|
44
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require_relative './../constants/constants'
|
5
|
+
require_relative './../utils/response_formatter'
|
6
|
+
|
7
|
+
# form data for various requests
|
8
|
+
module FormData
|
9
|
+
def self.item_by_id_data(id)
|
10
|
+
{
|
11
|
+
__requestVerificationToken: Constants::RVT,
|
12
|
+
mainKey: id,
|
13
|
+
usingCleint: 0
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.item_by_cat_data(cat, sub)
|
18
|
+
{
|
19
|
+
__requestVerificationToken: Constants::RVT,
|
20
|
+
mainCategory: cat,
|
21
|
+
subCategory: sub
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# get item by id and other item-related methods
|
27
|
+
module ItemEndpoints
|
28
|
+
include Constants
|
29
|
+
include FormData
|
30
|
+
include ResponseFormatter
|
31
|
+
|
32
|
+
def self.send_post_request(uri, data)
|
33
|
+
uri_params = URI.encode_www_form data
|
34
|
+
|
35
|
+
HTTParty.post(
|
36
|
+
uri,
|
37
|
+
body: uri_params,
|
38
|
+
headers: Constants::REQUEST_OPTS[:headers]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.respond_if_available(res)
|
43
|
+
return res.body if res.body
|
44
|
+
|
45
|
+
{
|
46
|
+
error: 'invalid response from the black desert api.',
|
47
|
+
response: res
|
48
|
+
}.to_json
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.item_by_id
|
52
|
+
lambda do |req|
|
53
|
+
item_id = req[:vars]['id']
|
54
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::MARKET_SUB_LIST}"
|
55
|
+
form = FormData.item_by_id_data item_id
|
56
|
+
res = send_post_request uri, form
|
57
|
+
|
58
|
+
ResponseFormatter.format_item_response(respond_if_available(res))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.items_by_category
|
63
|
+
lambda do |req|
|
64
|
+
item_cat = req[:vars]['cat']
|
65
|
+
item_sub = req[:vars]['sub']
|
66
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::WORLD_MARKET_LIST}"
|
67
|
+
form = FormData.item_by_cat_data item_cat, item_sub
|
68
|
+
res = send_post_request uri, form
|
69
|
+
|
70
|
+
ResponseFormatter.format_cat_response(respond_if_available(res))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
GET_ITEM_BY_ID = {
|
75
|
+
method: 'get',
|
76
|
+
path: '/item/:id',
|
77
|
+
responder: item_by_id,
|
78
|
+
opts: { content_type: 'application/json' }
|
79
|
+
}.values.freeze
|
80
|
+
|
81
|
+
GET_ITEM_BY_CATEGORY = {
|
82
|
+
method: 'get',
|
83
|
+
path: '/item/:cat/:sub',
|
84
|
+
responder: items_by_category,
|
85
|
+
opts: { content_type: 'application/json' }
|
86
|
+
}.values.freeze
|
87
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require_relative './../constants/constants'
|
5
|
+
require_relative './../utils/response_formatter'
|
6
|
+
|
7
|
+
# form data for various requests
|
8
|
+
module FormData
|
9
|
+
def self.price_list_data(id)
|
10
|
+
{
|
11
|
+
__requestVerificationToken: Constants::RVT,
|
12
|
+
mainKey: id,
|
13
|
+
subKey: 0,
|
14
|
+
keyType: 0,
|
15
|
+
isUp: true
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# get price list by id and name, and other price-related endpoints
|
21
|
+
module PriceEndpoints
|
22
|
+
include Constants
|
23
|
+
include FormData
|
24
|
+
include ResponseFormatter
|
25
|
+
|
26
|
+
def self.send_post_request(uri, data)
|
27
|
+
uri_params = URI.encode_www_form data
|
28
|
+
|
29
|
+
HTTParty.post(
|
30
|
+
uri,
|
31
|
+
body: uri_params,
|
32
|
+
headers: Constants::REQUEST_OPTS[:headers]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.respond_if_available(res)
|
37
|
+
return res.body if res.body
|
38
|
+
|
39
|
+
{
|
40
|
+
error: 'invalid response from the black desert api.',
|
41
|
+
response: res
|
42
|
+
}.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.price_list_by_id_and_name
|
46
|
+
lambda do |req|
|
47
|
+
item_id = req[:vars]['id']
|
48
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::MARKET_SELL_BUY_INFO}"
|
49
|
+
form = FormData.price_list_data item_id
|
50
|
+
res = send_post_request uri, form
|
51
|
+
|
52
|
+
ResponseFormatter.format_price_list_response(respond_if_available(res))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
GET_PRICE_LIST_BY_ID_AND_NAME = {
|
57
|
+
method: 'get',
|
58
|
+
path: '/prices/:id',
|
59
|
+
responder: price_list_by_id_and_name,
|
60
|
+
opts: { content_type: 'application/json' }
|
61
|
+
}.values.freeze
|
62
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require_relative './../constants/constants'
|
5
|
+
require_relative './../utils/response_formatter'
|
6
|
+
|
7
|
+
# form data for various requests
|
8
|
+
module FormData
|
9
|
+
def self.search_item_by_text_data(search_text)
|
10
|
+
"__requestVerificationToken=#{Constants::RVT}&searchText=#{search_text}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# search for item and other search-related methods
|
15
|
+
module SearchEndpoints
|
16
|
+
include Constants
|
17
|
+
include FormData
|
18
|
+
include ResponseFormatter
|
19
|
+
|
20
|
+
def self.send_post_request(uri, data)
|
21
|
+
uri_params = data
|
22
|
+
uri_params = URI.encode_www_form(data) if data.is_a? Hash
|
23
|
+
|
24
|
+
HTTParty.post(
|
25
|
+
uri,
|
26
|
+
body: uri_params,
|
27
|
+
headers: Constants::REQUEST_OPTS[:headers]
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.respond_if_available(res)
|
32
|
+
return res.body if res.body
|
33
|
+
|
34
|
+
{
|
35
|
+
error: 'invalid response from the black desert api.',
|
36
|
+
response: res
|
37
|
+
}.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.search_item_by_text
|
41
|
+
lambda do |req|
|
42
|
+
search_text = req[:params]['q']
|
43
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::MARKET_SEARCH_LIST}"
|
44
|
+
form = FormData.search_item_by_text_data search_text
|
45
|
+
res = send_post_request uri, form
|
46
|
+
|
47
|
+
ResponseFormatter.format_search_response(respond_if_available(res))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
SEARCH_ITEM_BY_TEXT = {
|
52
|
+
method: 'get',
|
53
|
+
path: '/search',
|
54
|
+
responder: search_item_by_text,
|
55
|
+
opts: { content_type: 'application/json' }
|
56
|
+
}.values.freeze
|
57
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require_relative './../constants/constants'
|
5
|
+
require_relative './../utils/response_formatter'
|
6
|
+
|
7
|
+
# get a list of items in the registration queue
|
8
|
+
module WaitlistEndpoints
|
9
|
+
include Constants
|
10
|
+
include FormData
|
11
|
+
include ResponseFormatter
|
12
|
+
|
13
|
+
def self.send_post_request(uri)
|
14
|
+
HTTParty.post(
|
15
|
+
uri,
|
16
|
+
headers: Constants::REQUEST_OPTS[:headers]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.respond_if_available(res)
|
21
|
+
return res.body if res.body
|
22
|
+
|
23
|
+
{
|
24
|
+
error: 'invalid response from the black desert api.',
|
25
|
+
response: res
|
26
|
+
}.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.waitlist_items
|
30
|
+
lambda do |_req|
|
31
|
+
uri = URI "#{Constants::ROOT_URL}#{Constants::MARKET_WAIT_LIST}"
|
32
|
+
res = send_post_request uri
|
33
|
+
|
34
|
+
ResponseFormatter.format_waitlist_response(respond_if_available(res))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
GET_WAITLIST_ITEMS = {
|
39
|
+
method: 'get',
|
40
|
+
path: '/waitlist',
|
41
|
+
responder: waitlist_items,
|
42
|
+
opts: { content_type: 'application/json' }
|
43
|
+
}.values.freeze
|
44
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require_relative './api_errors'
|
5
|
+
|
6
|
+
# rubocop:disable Lint/MissingCopEnableDirective, Metrics/MethodLength
|
7
|
+
|
8
|
+
# used to format responses from the black desert api
|
9
|
+
module ResponseFormatter
|
10
|
+
include ApiErrors
|
11
|
+
|
12
|
+
# returns an error message if the black desert api returns an error
|
13
|
+
def self.try_error(json)
|
14
|
+
return false if json['resultCode'].zero?
|
15
|
+
|
16
|
+
{
|
17
|
+
resultCode: json['resultCode'],
|
18
|
+
resultMsg: json['resultMsg']
|
19
|
+
}.to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
# for /item/:id endpoint
|
23
|
+
def self.format_item_response(data)
|
24
|
+
json = JSON.parse(data)
|
25
|
+
|
26
|
+
return try_error json if try_error json
|
27
|
+
|
28
|
+
{
|
29
|
+
data: json['detailList'].map do |e|
|
30
|
+
{
|
31
|
+
**e,
|
32
|
+
id: e['mainKey']
|
33
|
+
}.except('mainKey')
|
34
|
+
end
|
35
|
+
}.to_json
|
36
|
+
rescue NoMethodError, JSON::ParserError
|
37
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
38
|
+
end
|
39
|
+
|
40
|
+
# for /item/:cat/:sub endpoint
|
41
|
+
def self.format_cat_response(data)
|
42
|
+
json = JSON.parse(data)
|
43
|
+
|
44
|
+
return try_error json if try_error json
|
45
|
+
|
46
|
+
{
|
47
|
+
data: json['marketList'].map { |e| { **e, id: e['mainKey'] } }
|
48
|
+
}.to_json
|
49
|
+
rescue NoMethodError, JSON::ParserError
|
50
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
51
|
+
end
|
52
|
+
|
53
|
+
# for /search endpoint
|
54
|
+
def self.format_search_response(data)
|
55
|
+
json = JSON.parse(data)
|
56
|
+
|
57
|
+
return try_error json if try_error json
|
58
|
+
|
59
|
+
{
|
60
|
+
data: json['list'].map { |e| { **e, id: e['mainKey'] } }
|
61
|
+
}.to_json
|
62
|
+
rescue NoMethodError, JSON::ParserError
|
63
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
64
|
+
end
|
65
|
+
|
66
|
+
# for /prices endpoint
|
67
|
+
def self.format_price_list_response(data)
|
68
|
+
json = JSON.parse(data)
|
69
|
+
|
70
|
+
return try_error json if try_error json
|
71
|
+
|
72
|
+
{
|
73
|
+
data: {
|
74
|
+
pricePoints: json['priceList'],
|
75
|
+
buySellCounts: json['marketConditionList'],
|
76
|
+
**json
|
77
|
+
}.except('marketConditionList', 'priceList', 'resultCode')
|
78
|
+
}.to_json
|
79
|
+
rescue NoMethodError, JSON::ParserError
|
80
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
81
|
+
end
|
82
|
+
|
83
|
+
# for /hot endpoint
|
84
|
+
def self.format_hot_response(data)
|
85
|
+
json = JSON.parse(data)
|
86
|
+
|
87
|
+
return try_error json if try_error json
|
88
|
+
|
89
|
+
{
|
90
|
+
data: json['hotList'].map do |e|
|
91
|
+
{
|
92
|
+
**e,
|
93
|
+
id: e['mainKey'],
|
94
|
+
fluctuationDirection: e['fluctuationType'] == 2 ? 'up' : 'down'
|
95
|
+
}
|
96
|
+
end
|
97
|
+
}.to_json
|
98
|
+
rescue NoMethodError, JSON::ParserError
|
99
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
100
|
+
end
|
101
|
+
|
102
|
+
# for /waitlist endpoint
|
103
|
+
def self.format_waitlist_response(data)
|
104
|
+
json = JSON.parse(data)
|
105
|
+
|
106
|
+
return try_error json if try_error json
|
107
|
+
|
108
|
+
{
|
109
|
+
data: json['_waitList'].map do |e|
|
110
|
+
{
|
111
|
+
**e,
|
112
|
+
id: e['mainKey'],
|
113
|
+
waitEndTime: DateTime.strptime(e['_waitEndTime'].to_s, '%Q'),
|
114
|
+
waitEndTimestampMs: e['_waitEndTime'],
|
115
|
+
pricePerOne: e['_pricePerOne']
|
116
|
+
}.except('_pricePerOne', '_waitEndTime')
|
117
|
+
end
|
118
|
+
}.to_json
|
119
|
+
rescue NoMethodError, JSON::ParserError
|
120
|
+
ApiErrors::UNEXPECTED_BLACK_DESERT_RESPONSE
|
121
|
+
end
|
122
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,319 @@
|
|
1
|
+
# auciel
|
2
|
+
|
3
|
+
auciel is a wrapper for the black desert central market api. its purpose is to enable developers to get data from the market more easily, without having to figure out the strict (and odd) request structures required for the raw central market api.
|
4
|
+
|
5
|
+
built with the [tsurezure](https://github.com/jpegzilla/tsurezure) [![gem version](https://badge.fury.io/rb/tsurezure.svg)](https://badge.fury.io/rb/tsurezure) framework.
|
6
|
+
|
7
|
+
## usage
|
8
|
+
|
9
|
+
### installing from rubygems
|
10
|
+
|
11
|
+
_no gem yet_
|
12
|
+
|
13
|
+
### executable
|
14
|
+
|
15
|
+
_coming soon_
|
16
|
+
|
17
|
+
### using source code
|
18
|
+
|
19
|
+
download this repo. then, in your ruby script:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require_relative 'path/to/lib/auciel'
|
23
|
+
|
24
|
+
# default port is 8888
|
25
|
+
server = Auciel.new 8888
|
26
|
+
|
27
|
+
# server will be ready for requests at localhost:8888
|
28
|
+
server.listen
|
29
|
+
```
|
30
|
+
|
31
|
+
you can also just run `ruby main.rb` in the root directory and a server will start at `localhost:8888`.
|
32
|
+
|
33
|
+
## endpoints
|
34
|
+
|
35
|
+
### `/item/:id`
|
36
|
+
|
37
|
+
retrieves information about an item based on its id. equivalent to `GetWorldMarketSubList` in the raw api. example:
|
38
|
+
|
39
|
+
`GET https://localhost:8888/item/702`
|
40
|
+
|
41
|
+
```js
|
42
|
+
{
|
43
|
+
"data": [
|
44
|
+
{
|
45
|
+
"pricePerOne": 10700,
|
46
|
+
"totalTradeCount": 14269193,
|
47
|
+
"keyType": 0,
|
48
|
+
"mainKey": 702,
|
49
|
+
"subKey": 0,
|
50
|
+
"count": 0,
|
51
|
+
"name": "Elixir of Will",
|
52
|
+
"grade": 1,
|
53
|
+
"mainCategory": 35,
|
54
|
+
"subCategory": 2,
|
55
|
+
"chooseKey": 0,
|
56
|
+
"isGodrAyed": false
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
### `/item/:cat/:sub`
|
64
|
+
|
65
|
+
retrieves a list of items in a [category and subcategory](#item-categories). equivalent to `GetWorldMarketList` in the raw api. example:
|
66
|
+
|
67
|
+
`GET https://localhost:8888/item/35/1`
|
68
|
+
```js
|
69
|
+
{
|
70
|
+
"data": [
|
71
|
+
{
|
72
|
+
"mainKey": 504,
|
73
|
+
"sumCount": 0,
|
74
|
+
"name": "Melee AP Enhancer",
|
75
|
+
"grade": 0,
|
76
|
+
"isGodrAyed": false,
|
77
|
+
"minPrice": 93500,
|
78
|
+
"id": 504
|
79
|
+
},
|
80
|
+
{
|
81
|
+
"mainKey": 505,
|
82
|
+
"sumCount": 0,
|
83
|
+
"name": "Ranged AP Enhancer",
|
84
|
+
"grade": 0,
|
85
|
+
"isGodrAyed": false,
|
86
|
+
"minPrice": 93500,
|
87
|
+
"id": 505
|
88
|
+
},
|
89
|
+
|
90
|
+
...
|
91
|
+
|
92
|
+
```
|
93
|
+
|
94
|
+
### `/search?q=<search text>`
|
95
|
+
|
96
|
+
retrieves a list of items whose names match the provided **url encoded** search string. equivalent to `GetWorldMarketSearchList` in the raw api. example:
|
97
|
+
|
98
|
+
`GET http://localhost:8888/search?q=blood`
|
99
|
+
```js
|
100
|
+
{
|
101
|
+
"data": [
|
102
|
+
{
|
103
|
+
"mainKey": 3040,
|
104
|
+
"sumCount": 1,
|
105
|
+
"totalSumCount": 77500000,
|
106
|
+
"name": "Muskan's Bloody Steel Helmet",
|
107
|
+
"grade": 2,
|
108
|
+
"isGodrAyed": false,
|
109
|
+
"id": 3040
|
110
|
+
},
|
111
|
+
{
|
112
|
+
"mainKey": 4453,
|
113
|
+
"sumCount": 2674,
|
114
|
+
"totalSumCount": 236000,
|
115
|
+
"name": "Blood Ruby",
|
116
|
+
"grade": 0,
|
117
|
+
"isGodrAyed": false,
|
118
|
+
"id": 4453
|
119
|
+
},
|
120
|
+
{
|
121
|
+
"mainKey": 5005,
|
122
|
+
"sumCount": 30309,
|
123
|
+
"totalSumCount": 19000,
|
124
|
+
"name": "Bloody Tree Knot",
|
125
|
+
"grade": 0,
|
126
|
+
"isGodrAyed": false,
|
127
|
+
"id": 5005
|
128
|
+
},
|
129
|
+
|
130
|
+
...
|
131
|
+
|
132
|
+
```
|
133
|
+
|
134
|
+
### `/prices/:id`
|
135
|
+
|
136
|
+
retrieves a detailed price list for a specified item id. equivalent to `GetItemSellBuyInfo` in the raw api, and similar to `GetBiddingInfoList`. example:
|
137
|
+
|
138
|
+
`GET http://localhost:8888/prices/702`
|
139
|
+
```js
|
140
|
+
{
|
141
|
+
"data": {
|
142
|
+
"pricePoints": [
|
143
|
+
13500,
|
144
|
+
13600,
|
145
|
+
13700,
|
146
|
+
13800,
|
147
|
+
13900,
|
148
|
+
14000,
|
149
|
+
14100,
|
150
|
+
14200,
|
151
|
+
14300,
|
152
|
+
14400,
|
153
|
+
14500,
|
154
|
+
14600
|
155
|
+
],
|
156
|
+
"buySellCounts": [
|
157
|
+
{
|
158
|
+
"sellCount": 0,
|
159
|
+
"buyCount": 0,
|
160
|
+
"pricePerOne": 12500
|
161
|
+
},
|
162
|
+
{
|
163
|
+
"sellCount": 0,
|
164
|
+
"buyCount": 0,
|
165
|
+
"pricePerOne": 12600
|
166
|
+
},
|
167
|
+
{
|
168
|
+
"sellCount": 0,
|
169
|
+
"buyCount": 0,
|
170
|
+
"pricePerOne": 12700
|
171
|
+
},
|
172
|
+
|
173
|
+
...
|
174
|
+
|
175
|
+
```
|
176
|
+
|
177
|
+
### `/hot`
|
178
|
+
|
179
|
+
retrieves a list of currently popular items. equivalent to `GetWorldMarketHotList` in the raw api. example:
|
180
|
+
|
181
|
+
`GET http://localhost:8888/hot`
|
182
|
+
```js
|
183
|
+
{
|
184
|
+
"data": [
|
185
|
+
{
|
186
|
+
"subtype": 0,
|
187
|
+
"pricePerOne": 66000,
|
188
|
+
"totalTradeCount": 340891,
|
189
|
+
"fluctuationType": 2,
|
190
|
+
"fluctuationPrice": 12500,
|
191
|
+
"keyType": 0,
|
192
|
+
"mainKey": 641,
|
193
|
+
"subKey": 0,
|
194
|
+
"count": 0,
|
195
|
+
"name": "[Party] Elixir of Fury",
|
196
|
+
"grade": 1,
|
197
|
+
"mainCategory": 35,
|
198
|
+
"subCategory": 0,
|
199
|
+
"chooseKey": 0,
|
200
|
+
"isGodrAyed": false,
|
201
|
+
"id": 641,
|
202
|
+
"fluctuationDirection": "up"
|
203
|
+
},
|
204
|
+
{
|
205
|
+
"subtype": 0,
|
206
|
+
"pricePerOne": 47800,
|
207
|
+
"totalTradeCount": 1780501,
|
208
|
+
"fluctuationType": 1,
|
209
|
+
"fluctuationPrice": 12200,
|
210
|
+
"keyType": 0,
|
211
|
+
"mainKey": 683,
|
212
|
+
"subKey": 0,
|
213
|
+
"count": 8446,
|
214
|
+
"name": "Surging Energy Elixir",
|
215
|
+
"grade": 2,
|
216
|
+
"mainCategory": 35,
|
217
|
+
"subCategory": 0,
|
218
|
+
"chooseKey": 0,
|
219
|
+
"isGodrAyed": false,
|
220
|
+
"id": 683,
|
221
|
+
"fluctuationDirection": "down"
|
222
|
+
},
|
223
|
+
|
224
|
+
...
|
225
|
+
|
226
|
+
```
|
227
|
+
|
228
|
+
### `/waitlist`
|
229
|
+
|
230
|
+
retrieves a list of currently waitlisted items. equivalent to `GetWorldMarketWaitList` in the raw api. example:
|
231
|
+
|
232
|
+
`GET http://localhost:8888/waitlist`
|
233
|
+
```js
|
234
|
+
{
|
235
|
+
"data": [
|
236
|
+
{
|
237
|
+
"keyType": 0,
|
238
|
+
"mainKey": 11103,
|
239
|
+
"subKey": 0,
|
240
|
+
"count": 0,
|
241
|
+
"name": "Urugon's Shoes",
|
242
|
+
"grade": 3,
|
243
|
+
"mainCategory": 15,
|
244
|
+
"subCategory": 0,
|
245
|
+
"chooseKey": 20,
|
246
|
+
"isGodrAyed": false,
|
247
|
+
"id": 11103,
|
248
|
+
"waitEndTime": "2023-03-04T20:03:55+00:00",
|
249
|
+
"waitEndTimestampMs": 1677960235417,
|
250
|
+
"pricePerOne": 12400000000
|
251
|
+
},
|
252
|
+
{
|
253
|
+
"keyType": 0,
|
254
|
+
"mainKey": 12230,
|
255
|
+
"subKey": 0,
|
256
|
+
"count": 0,
|
257
|
+
"name": "Basilisk's Belt",
|
258
|
+
"grade": 3,
|
259
|
+
"mainCategory": 20,
|
260
|
+
"subCategory": 0,
|
261
|
+
"chooseKey": 5,
|
262
|
+
"isGodrAyed": false,
|
263
|
+
"id": 12230,
|
264
|
+
"waitEndTime": "2023-03-04T20:11:04+00:00",
|
265
|
+
"waitEndTimestampMs": 1677960664373,
|
266
|
+
"pricePerOne": 53000000000
|
267
|
+
},
|
268
|
+
|
269
|
+
...
|
270
|
+
|
271
|
+
```
|
272
|
+
|
273
|
+
## item categories
|
274
|
+
|
275
|
+
here's a list of item types with their categories / subcategories for the `item/:cat/:sub` endpoint. for example, you could retrieve a list of foods at `item/35/4`.
|
276
|
+
|
277
|
+
item type | category | subcategories
|
278
|
+
----------------------|----------|--------------
|
279
|
+
main weapon | 1 | 1-20
|
280
|
+
sub-weapon | 5 | 1-18
|
281
|
+
awakening | 10 | 1-24
|
282
|
+
armor | 15 | 1-6
|
283
|
+
accessories | 20 | 1-4
|
284
|
+
lightstone | 85 | 1-5
|
285
|
+
material | 25 | 1-8
|
286
|
+
enhancement / upgrade | 30 | 1-2
|
287
|
+
consumables | 35 | 1-8
|
288
|
+
life tools | 40 | 1-10
|
289
|
+
alchemy stone | 45 | 1-4
|
290
|
+
magic crystal | 50 | 1-4
|
291
|
+
pearl item | 55 | 1-8
|
292
|
+
dye | 60 | 1-8
|
293
|
+
mount | 65 | 1-13
|
294
|
+
ship | 70 | 1-9
|
295
|
+
wagon | 75 | 1-6
|
296
|
+
furniture | 80 | 1-9
|
297
|
+
|
298
|
+
## item grades
|
299
|
+
|
300
|
+
here's a list of item grades with their corresponding `grade` numbers as returned from the api.
|
301
|
+
|
302
|
+
grade (border color) | number (`grade`)
|
303
|
+
---------------------|-----------------
|
304
|
+
white | 0
|
305
|
+
green | 1
|
306
|
+
blue | 2
|
307
|
+
yellow | 3
|
308
|
+
orange | 4
|
309
|
+
|
310
|
+
## other info
|
311
|
+
|
312
|
+
auciel is the name of the central market director at old wisdom tree.
|
313
|
+
|
314
|
+
## stuff I don't know yet
|
315
|
+
|
316
|
+
- [ ] what is `chooseKey` on certain items?
|
317
|
+
- [ ] does `keyType` do anything?
|
318
|
+
- [ ] why does every item have an `isGodrAyed` property, even non-godr-ayeable items?
|
319
|
+
- [ ] can you get a list of every single item?
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: auciel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jpegzilla
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tsurezure
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.36
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.36
|
27
|
+
description: an api used to get data from the black desert online central market.
|
28
|
+
email: eris@jpegzilla.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- LICENSE
|
34
|
+
- lib/auciel.rb
|
35
|
+
- lib/constants/constants.rb
|
36
|
+
- lib/endpoints/hot.rb
|
37
|
+
- lib/endpoints/item.rb
|
38
|
+
- lib/endpoints/prices.rb
|
39
|
+
- lib/endpoints/search.rb
|
40
|
+
- lib/endpoints/waitlist.rb
|
41
|
+
- lib/utils/api_errors.rb
|
42
|
+
- lib/utils/response_formatter.rb
|
43
|
+
- readme.md
|
44
|
+
homepage: https://github.com/jpegzilla/auciel
|
45
|
+
licenses:
|
46
|
+
- MIT
|
47
|
+
metadata: {}
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubygems_version: 3.4.7
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: this is a wrapper for black desert's marketplace api.
|
67
|
+
test_files: []
|