229 lines
5.8 KiB
Ruby
229 lines
5.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Dataminer
|
|
include HTTParty
|
|
|
|
BOT_UID = '39094985'
|
|
GAME_VERSION = '1741068713'
|
|
|
|
base_uri 'https://game.granbluefantasy.jp'
|
|
format :json
|
|
|
|
HEADERS = {
|
|
'Accept' => 'application/json, text/javascript, */*; q=0.01',
|
|
'Accept-Language' => 'en-US,en;q=0.9',
|
|
'Accept-Encoding' => 'gzip, deflate, br, zstd',
|
|
'Content-Type' => 'application/json',
|
|
'DNT' => '1',
|
|
'Origin' => 'https://game.granbluefantasy.jp',
|
|
'Referer' => 'https://game.granbluefantasy.jp/',
|
|
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
|
|
'X-Requested-With' => 'XMLHttpRequest'
|
|
}.freeze
|
|
|
|
attr_reader :page, :cookies
|
|
|
|
def initialize(page:, access_token:, wing:, midship:, t: 'dummy')
|
|
@page = page
|
|
@cookies = {
|
|
access_gbtk: access_token,
|
|
wing: wing,
|
|
t: t,
|
|
midship: midship
|
|
}
|
|
end
|
|
|
|
def fetch
|
|
timestamp = Time.now.to_i * 1000
|
|
response = self.class.post(
|
|
"/#{page}?_=#{timestamp}&t=#{timestamp}&uid=#{BOT_UID}",
|
|
headers: HEADERS.merge(
|
|
'Cookie' => format_cookies,
|
|
'X-VERSION' => GAME_VERSION
|
|
)
|
|
)
|
|
|
|
raise AuthenticationError if auth_failed?(response)
|
|
|
|
response
|
|
end
|
|
|
|
def fetch_character(granblue_id)
|
|
timestamp = Time.now.to_i * 1000
|
|
url = "/archive/npc_detail?_=#{timestamp}&t=#{timestamp}&uid=#{BOT_UID}"
|
|
body = {
|
|
special_token: nil,
|
|
user_id: BOT_UID,
|
|
kind_name: '0',
|
|
attribute: '0',
|
|
event_id: nil,
|
|
story_id: nil,
|
|
style: 1,
|
|
character_id: granblue_id
|
|
}
|
|
|
|
response = fetch_detail(url, body)
|
|
update_game_data('Character', granblue_id, response) if response
|
|
response
|
|
end
|
|
|
|
def fetch_weapon(granblue_id)
|
|
timestamp = Time.now.to_i * 1000
|
|
url = "/archive/weapon_detail?_=#{timestamp}&t=#{timestamp}&uid=#{BOT_UID}"
|
|
body = {
|
|
special_token: nil,
|
|
user_id: BOT_UID,
|
|
kind_name: '0',
|
|
attribute: '0',
|
|
event_id: nil,
|
|
story_id: nil,
|
|
weapon_id: granblue_id
|
|
}
|
|
|
|
response = fetch_detail(url, body)
|
|
update_game_data('Weapon', granblue_id, response) if response
|
|
response
|
|
end
|
|
|
|
def fetch_summon(granblue_id)
|
|
timestamp = Time.now.to_i * 1000
|
|
url = "/archive/summon_detail?_=#{timestamp}&t=#{timestamp}&uid=#{BOT_UID}"
|
|
body = {
|
|
special_token: nil,
|
|
user_id: BOT_UID,
|
|
kind_name: '0',
|
|
attribute: '0',
|
|
event_id: nil,
|
|
story_id: nil,
|
|
summon_id: granblue_id
|
|
}
|
|
|
|
response = fetch_detail(url, body)
|
|
update_game_data('Summon', granblue_id, response) if response
|
|
response
|
|
end
|
|
|
|
# Public batch processing methods
|
|
def fetch_all_characters
|
|
process_all_records('Character')
|
|
end
|
|
|
|
def fetch_all_weapons
|
|
process_all_records('Weapon')
|
|
end
|
|
|
|
def fetch_all_summons
|
|
process_all_records('Summon')
|
|
end
|
|
|
|
private
|
|
|
|
def format_cookies
|
|
cookies.map { |k, v| "#{k}=#{v}" }.join('; ')
|
|
end
|
|
|
|
def auth_failed?(response)
|
|
return true if response.code != 200
|
|
|
|
begin
|
|
parsed = JSON.parse(response.body)
|
|
parsed.is_a?(Hash) && parsed['auth_status'] == 'require_auth'
|
|
rescue JSON::ParserError
|
|
true
|
|
end
|
|
end
|
|
|
|
def fetch_detail(url, body)
|
|
puts "\n=== Request Details ==="
|
|
puts "URL: #{url}"
|
|
puts 'Headers:'
|
|
puts HEADERS.merge(
|
|
'Cookie' => format_cookies,
|
|
'X-VERSION' => GAME_VERSION
|
|
).inspect
|
|
puts 'Body:'
|
|
puts body.to_json
|
|
puts '===================='
|
|
|
|
response = self.class.post(
|
|
url,
|
|
headers: HEADERS.merge(
|
|
'Cookie' => format_cookies,
|
|
'X-VERSION' => GAME_VERSION
|
|
),
|
|
body: body.to_json
|
|
)
|
|
|
|
puts "\n=== Response Details ==="
|
|
puts "Response code: #{response.code}"
|
|
puts 'Response headers:'
|
|
puts response.headers.inspect
|
|
puts 'Raw response body:'
|
|
puts response.body.inspect
|
|
begin
|
|
puts 'Parsed response body (if JSON):'
|
|
puts JSON.parse(response.body).inspect
|
|
rescue JSON::ParserError => e
|
|
puts "Could not parse as JSON: #{e.message}"
|
|
end
|
|
puts '======================'
|
|
|
|
raise AuthenticationError if auth_failed?(response)
|
|
|
|
JSON.parse(response.body)
|
|
end
|
|
|
|
def update_game_data(model_name, granblue_id, response_data)
|
|
return unless response_data.is_a?(Hash)
|
|
|
|
model = Object.const_get(model_name)
|
|
record = model.find_by(granblue_id: granblue_id)
|
|
|
|
if record
|
|
record.update(game_raw_en: response_data)
|
|
puts "Updated #{model_name} #{granblue_id}"
|
|
else
|
|
puts "#{model_name} with granblue_id #{granblue_id} not found in database"
|
|
end
|
|
rescue StandardError => e
|
|
puts "Error updating #{model_name} #{granblue_id}: #{e.message}"
|
|
end
|
|
|
|
def process_all_records(model_name)
|
|
model = Object.const_get(model_name)
|
|
total = model.count
|
|
success_count = 0
|
|
error_count = 0
|
|
|
|
puts "Starting to fetch #{total} #{model_name.downcase}s..."
|
|
|
|
model.find_each do |record|
|
|
puts "\nProcessing #{model_name} #{record.granblue_id} (#{success_count + error_count + 1}/#{total})"
|
|
|
|
response = case model_name
|
|
when 'Character'
|
|
fetch_character(record.granblue_id)
|
|
when 'Weapon'
|
|
fetch_weapon(record.granblue_id)
|
|
when 'Summon'
|
|
fetch_summon(record.granblue_id)
|
|
end
|
|
|
|
success_count += 1
|
|
puts "Successfully processed #{model_name} #{record.granblue_id}"
|
|
|
|
# Add a small delay to avoid overwhelming the server
|
|
sleep(1)
|
|
rescue StandardError => e
|
|
error_count += 1
|
|
puts "Error processing #{model_name} #{record.granblue_id}: #{e.message}"
|
|
end
|
|
|
|
puts "\nProcessing complete!"
|
|
puts "Total: #{total}"
|
|
puts "Successful: #{success_count}"
|
|
puts "Failed: #{error_count}"
|
|
end
|
|
|
|
class AuthenticationError < StandardError; end
|
|
end
|