Skip to content

Commit de209f3

Browse files
committed
feat: implement fetch_account_by_puuid method
1 parent 9d21c04 commit de209f3

5 files changed

Lines changed: 140 additions & 21 deletions

File tree

app/modules/players/jobs/sync_player_from_riot_job.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ def perform(player_id)
3030
fetch_summoner_by_name(player.summoner_name, region, riot_api_key)
3131
end
3232

33+
# Fetch account data to get current gameName#tagLine
34+
account_data = fetch_account_by_puuid(player.riot_puuid, region, riot_api_key)
35+
3336
# Use PUUID for league endpoint (workaround for Riot API bug where summoner_data['id'] is nil)
3437
# See: https://github.com/RiotGames/developer-relations/issues/1092
3538
ranked_data = fetch_ranked_stats_by_puuid(player.riot_puuid, region, riot_api_key)
@@ -43,6 +46,15 @@ def perform(player_id)
4346
last_sync_at: Time.current
4447
}
4548

49+
# Update summoner_name if it has changed
50+
if account_data['gameName'].present? && account_data['tagLine'].present?
51+
new_summoner_name = "#{account_data['gameName']}##{account_data['tagLine']}"
52+
if player.summoner_name != new_summoner_name
53+
Rails.logger.info("Player #{player.id} name changed: #{player.summoner_name}#{new_summoner_name}")
54+
update_data[:summoner_name] = new_summoner_name
55+
end
56+
end
57+
4658
solo_queue = ranked_data.find { |q| q['queueType'] == 'RANKED_SOLO_5x5' }
4759
if solo_queue
4860
update_data.merge!({
@@ -76,6 +88,36 @@ def perform(player_id)
7688

7789
private
7890

91+
def fetch_account_by_puuid(puuid, region, api_key)
92+
require 'net/http'
93+
require 'json'
94+
95+
# Determine regional endpoint
96+
regional_endpoint = case region.downcase
97+
when 'br1', 'na1', 'lan', 'las1'
98+
'americas'
99+
when 'euw1', 'eune1', 'ru', 'tr1'
100+
'europe'
101+
when 'kr', 'jp1', 'oce1'
102+
'asia'
103+
else
104+
'americas'
105+
end
106+
107+
url = "https://#{regional_endpoint}.api.riotgames.com/riot/account/v1/accounts/by-puuid/#{puuid}"
108+
uri = URI(url)
109+
request = Net::HTTP::Get.new(uri)
110+
request['X-Riot-Token'] = api_key
111+
112+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
113+
http.request(request)
114+
end
115+
116+
raise "Riot API Error: #{response.code} - #{response.body}" unless response.is_a?(Net::HTTPSuccess)
117+
118+
JSON.parse(response.body)
119+
end
120+
79121
def fetch_summoner_by_name(summoner_name, region, api_key)
80122
require 'net/http'
81123
require 'json'

app/modules/players/services/riot_sync_service.rb

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,18 @@ def sync_player(player, import_matches: true)
118118
return { success: false, error: 'Player missing PUUID' } if player.riot_puuid.blank?
119119

120120
begin
121-
# 1. Fetch current rank and profile
121+
# 1. Fetch account info to get current gameName#tagLine
122+
account_data = fetch_account_by_puuid(player.riot_puuid)
123+
124+
# 2. Fetch current rank and profile
122125
summoner_data = fetch_summoner_by_puuid(player.riot_puuid)
123126
# Use PUUID to fetch rank data (summoner_id is no longer returned by Riot API)
124127
rank_data = fetch_rank_data_by_puuid(player.riot_puuid)
125128

126-
# 2. Update player with fresh data
127-
update_player_from_riot(player, summoner_data, rank_data)
129+
# 3. Update player with fresh data (including summoner_name if changed)
130+
update_player_from_riot(player, account_data, summoner_data, rank_data)
128131

129-
# 3. Optionally fetch recent matches
132+
# 4. Optionally fetch recent matches
130133
matches_imported = 0
131134
matches_imported = import_player_matches(player, count: 20) if import_matches
132135

@@ -146,6 +149,19 @@ def sync_player(player, import_matches: true)
146149
end
147150
end
148151

152+
# Fetch account info (gameName, tagLine) by PUUID
153+
def fetch_account_by_puuid(puuid)
154+
regional_endpoint = get_regional_endpoint(region)
155+
156+
# Use whitelisted host to prevent SSRF
157+
uri = URI::HTTPS.build(
158+
host: regional_api_host(regional_endpoint),
159+
path: "/riot/account/v1/accounts/by-puuid/#{ERB::Util.url_encode(puuid)}"
160+
)
161+
response = make_request(uri.to_s)
162+
JSON.parse(response.body)
163+
end
164+
149165
# Fetch summoner by PUUID
150166
def fetch_summoner_by_puuid(puuid)
151167
# Use whitelisted host to prevent SSRF
@@ -216,26 +232,26 @@ def import_player_matches(player, count: 20)
216232

217233
# Search for a player by Riot ID (GameName#TagLine)
218234
def search_riot_id(game_name, tag_line)
219-
Rails.logger.info("🎮 Searching for Riot ID: #{game_name}##{tag_line}")
220-
Rails.logger.info("🌍 Region: #{region}")
235+
Rails.logger.info("Searching for Riot ID: #{game_name}##{tag_line}")
236+
Rails.logger.info("Region: #{region}")
221237

222238
regional_endpoint = get_regional_endpoint(region)
223-
Rails.logger.info("🗺️ Regional endpoint: #{regional_endpoint}")
239+
Rails.logger.info("Regional endpoint: #{regional_endpoint}")
224240

225241
# Use whitelisted host to prevent SSRF
226242
# Use ERB::Util.url_encode instead of CGI.escape to properly encode spaces as %20 (not +)
227243
encoded_game_name = ERB::Util.url_encode(game_name)
228244
encoded_tag_line = ERB::Util.url_encode(tag_line)
229245

230-
Rails.logger.info("📝 Encoded game_name: '#{game_name}' -> '#{encoded_game_name}'")
231-
Rails.logger.info("📝 Encoded tag_line: '#{tag_line}' -> '#{encoded_tag_line}'")
246+
Rails.logger.info("Encoded game_name: '#{game_name}' -> '#{encoded_game_name}'")
247+
Rails.logger.info("Encoded tag_line: '#{tag_line}' -> '#{encoded_tag_line}'")
232248

233249
uri = URI::HTTPS.build(
234250
host: regional_api_host(regional_endpoint),
235251
path: "/riot/account/v1/accounts/by-riot-id/#{encoded_game_name}/#{encoded_tag_line}"
236252
)
237253

238-
Rails.logger.info("🔗 Full URL: #{uri}")
254+
Rails.logger.info("Full URL: #{uri}")
239255

240256
response = make_request(uri.to_s)
241257
account_data = JSON.parse(response.body)
@@ -254,9 +270,9 @@ def search_riot_id(game_name, tag_line)
254270
rank_data: rank_data
255271
}
256272
rescue StandardError => e
257-
Rails.logger.error("Failed to search Riot ID #{game_name}##{tag_line}: #{e.message}")
258-
Rails.logger.error("Exception class: #{e.class.name}")
259-
Rails.logger.error("Backtrace: #{e.backtrace.first(5).join("\n")}")
273+
Rails.logger.error("Failed to search Riot ID #{game_name}##{tag_line}: #{e.message}")
274+
Rails.logger.error("Exception class: #{e.class.name}")
275+
Rails.logger.error("Backtrace: #{e.backtrace.first(5).join("\n")}")
260276
nil
261277
end
262278

@@ -302,7 +318,7 @@ def check_existing_player(puuid, summoner_name, riot_data)
302318
# Log security warning when attempting to import player from another org
303319
def log_security_warning(summoner_name, riot_data, existing_player)
304320
Rails.logger.warn(
305-
"⚠️ SECURITY: Attempt to import player #{summoner_name} " \
321+
" SECURITY: Attempt to import player #{summoner_name} " \
306322
"(PUUID: #{riot_data[:puuid]}) that belongs to organization " \
307323
"#{existing_player.organization.name} by organization #{organization.name}"
308324
)
@@ -372,16 +388,16 @@ def make_request(url)
372388
request['X-Riot-Token'] = api_key
373389

374390
# Debug logging
375-
Rails.logger.info("🔍 Making Riot API request to: #{uri}")
376-
Rails.logger.info("🔑 API Key present: #{api_key.present?} (length: #{api_key&.length || 0})")
391+
Rails.logger.info(" Making Riot API request to: #{uri}")
392+
Rails.logger.info(" API Key present: #{api_key.present?} (length: #{api_key&.length || 0})")
377393

378394
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
379395
http.request(request)
380396
end
381397

382398
unless response.is_a?(Net::HTTPSuccess)
383399
error_message = "Riot API Error: #{response.code} - #{response.body}"
384-
Rails.logger.error("Riot API Error - URL: #{uri} - Status: #{response.code} - Body: #{response.body}")
400+
Rails.logger.error("Riot API Error - URL: #{uri} - Status: #{response.code} - Body: #{response.body}")
385401

386402
# Create custom exception with status code for better error handling
387403
error = RiotApiError.new(error_message)
@@ -390,13 +406,13 @@ def make_request(url)
390406
raise error
391407
end
392408

393-
Rails.logger.info(" Riot API request successful: #{response.code}")
409+
Rails.logger.info(" Riot API request successful: #{response.code}")
394410
response
395411
end
396412

397413
# Update player with Riot data
398-
def update_player_from_riot(player, summoner_data, rank_data)
399-
player.update!(
414+
def update_player_from_riot(player, account_data, summoner_data, rank_data)
415+
update_attrs = {
400416
summoner_level: summoner_data['summonerLevel'],
401417
profile_icon_id: summoner_data['profileIconId'],
402418
solo_queue_tier: rank_data['tier'],
@@ -406,7 +422,18 @@ def update_player_from_riot(player, summoner_data, rank_data)
406422
solo_queue_losses: rank_data['losses'],
407423
last_sync_at: Time.current,
408424
sync_status: 'success'
409-
)
425+
}
426+
427+
# Update summoner_name if it has changed
428+
if account_data['gameName'].present? && account_data['tagLine'].present?
429+
new_summoner_name = "#{account_data['gameName']}##{account_data['tagLine']}"
430+
if player.summoner_name != new_summoner_name
431+
Rails.logger.info(" Player #{player.id} name changed: #{player.summoner_name}#{new_summoner_name}")
432+
update_attrs[:summoner_name] = new_summoner_name
433+
end
434+
end
435+
436+
player.update!(update_attrs)
410437
end
411438

412439
# Import a match from Riot data

app/modules/riot_integration/services/riot_api_service.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ def get_summoner_by_name(summoner_name:, region:)
3838
parse_summoner_response(response)
3939
end
4040

41+
def get_account_by_puuid(puuid:, region:)
42+
regional_route = regional_route_for_region(region)
43+
url = "https://#{regional_route}.api.riotgames.com/riot/account/v1/accounts/by-puuid/#{puuid}"
44+
45+
response = make_request(url)
46+
parse_account_response(response)
47+
end
48+
4149
def get_summoner_by_puuid(puuid:, region:)
4250
platform = platform_for_region(region)
4351
url = "https://#{platform}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/#{puuid}"
@@ -179,6 +187,15 @@ def normalize_region(region)
179187
platform_to_region[normalized] || normalized
180188
end
181189

190+
def parse_account_response(response)
191+
data = JSON.parse(response.body)
192+
{
193+
puuid: data['puuid'],
194+
game_name: data['gameName'],
195+
tag_line: data['tagLine']
196+
}
197+
end
198+
182199
def parse_summoner_response(response)
183200
data = JSON.parse(response.body)
184201
{

app/modules/scouting/jobs/sync_scouting_target_job.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ def perform(scouting_target_id)
2626
)
2727
end
2828

29+
# Fetch account data to update summoner_name if changed
30+
if target.riot_puuid.present?
31+
account_data = riot_service.get_account_by_puuid(
32+
puuid: target.riot_puuid,
33+
region: target.region
34+
)
35+
36+
if account_data[:game_name].present? && account_data[:tag_line].present?
37+
new_summoner_name = "#{account_data[:game_name]}##{account_data[:tag_line]}"
38+
if target.summoner_name != new_summoner_name
39+
Rails.logger.info("Scouting target #{target.id} name changed: #{target.summoner_name}#{new_summoner_name}")
40+
target.update!(summoner_name: new_summoner_name)
41+
end
42+
end
43+
end
44+
2945
if target.riot_summoner_id.present?
3046
league_data = riot_service.get_league_entries(
3147
summoner_id: target.riot_summoner_id,

app/services/riot_api_service.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ def get_summoner_by_name(summoner_name:, region:)
7575
parse_summoner_response(response)
7676
end
7777

78+
def get_account_by_puuid(puuid:, region:)
79+
regional_route = regional_route_for_region(region)
80+
url = "https://#{regional_route}.api.riotgames.com/riot/account/v1/accounts/by-puuid/#{puuid}"
81+
82+
response = make_request(url)
83+
parse_account_response(response)
84+
end
85+
7886
def get_summoner_by_puuid(puuid:, region:)
7987
platform = platform_for_region(region)
8088
url = "https://#{platform}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/#{puuid}"
@@ -190,6 +198,15 @@ def regional_route_for_region(region)
190198
REGIONS.dig(clean_region, :region) || raise(RiotApiError, "Unknown region: #{region}")
191199
end
192200

201+
def parse_account_response(response)
202+
data = JSON.parse(response.body)
203+
{
204+
puuid: data['puuid'],
205+
game_name: data['gameName'],
206+
tag_line: data['tagLine']
207+
}
208+
end
209+
193210
def parse_summoner_response(response)
194211
data = JSON.parse(response.body)
195212
{

0 commit comments

Comments
 (0)