Steam persona display names + admin backfill #9
@@ -4,6 +4,8 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Prospect.Server.Api.Services.Database;
|
||||
using Prospect.Server.Api.Services.Database.Models;
|
||||
using Prospect.Server.Api.Services.Steam;
|
||||
using Prospect.Server.Api.Services.UserData;
|
||||
|
||||
namespace Prospect.Server.Api.Controllers;
|
||||
@@ -18,14 +20,16 @@ public class AdminController : ControllerBase
|
||||
private readonly DbEntityService _entityService;
|
||||
private readonly DbUserDataService _userDataService;
|
||||
private readonly TitleDataService _titleDataService;
|
||||
private readonly SteamWebApiService _steamWebApi;
|
||||
|
||||
public AdminController(DbUserService userService, DbEntityService entityService,
|
||||
DbUserDataService userDataService, TitleDataService titleDataService)
|
||||
DbUserDataService userDataService, TitleDataService titleDataService, SteamWebApiService steamWebApi)
|
||||
{
|
||||
_userService = userService;
|
||||
_entityService = entityService;
|
||||
_userDataService = userDataService;
|
||||
_titleDataService = titleDataService;
|
||||
_steamWebApi = steamWebApi;
|
||||
}
|
||||
|
||||
// ---------- pages ----------
|
||||
@@ -82,7 +86,8 @@ public class AdminController : ControllerBase
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"<form method='get' class='search'><input name='q' placeholder='Rechercher nom / id / steam…' value='{Esc(q ?? "")}'/><button class='btn'>Rechercher</button></form>");
|
||||
sb.Append($"<form method='get' class='search'><input name='q' placeholder='Rechercher nom / id / steam…' value='{Esc(q ?? "")}'/><button class='btn'>Rechercher</button>" +
|
||||
"<a class='btn' href='/admin/backfill-names' title='Récupère les pseudos Steam des comptes sans nom'>Backfill pseudos Steam</a></form>");
|
||||
sb.Append($"<p class='muted'>{users.Count} joueur(s)</p>");
|
||||
sb.Append("<table><thead><tr><th>DisplayName</th><th>PlayFab Id</th><th>Entity Id</th><th>Auth</th><th></th></tr></thead><tbody>");
|
||||
foreach (var u in users.OrderBy(u => u.DisplayName))
|
||||
@@ -179,6 +184,38 @@ public class AdminController : ControllerBase
|
||||
return Html(Layout("TitleData · " + key, $"<h2 class='mono'>{Esc(key)}</h2>" + body));
|
||||
}
|
||||
|
||||
[HttpGet("backfill-names")]
|
||||
public async Task<IActionResult> BackfillNames()
|
||||
{
|
||||
if (!IsLan()) return Denied();
|
||||
if (!_steamWebApi.Enabled)
|
||||
return Html(Layout("Backfill", "<p class='muted'>Steam Web API non configurée (clé absente).</p><p><a class='btn' href='/admin/players'>← Joueurs</a></p>"));
|
||||
|
||||
var users = await _userService.GetAllAsync();
|
||||
int updated = 0, skipped = 0, failed = 0;
|
||||
var rows = new StringBuilder();
|
||||
foreach (var u in users)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(u.DisplayName) && u.DisplayName != "Unknown") { skipped++; continue; }
|
||||
var steam = u.Auth?.FirstOrDefault(a => a.Type == PlayFabUserAuthType.Steam)?.Key;
|
||||
if (string.IsNullOrEmpty(steam)) { skipped++; continue; }
|
||||
|
||||
var persona = await _steamWebApi.GetPersonaNameAsync(steam);
|
||||
if (!string.IsNullOrWhiteSpace(persona))
|
||||
{
|
||||
await _userService.UpdateDisplayNameAsync(u.Id, persona);
|
||||
updated++;
|
||||
rows.Append($"<tr><td class='mono small'>{Esc(u.Id)}</td><td>{Esc(persona)}</td></tr>");
|
||||
}
|
||||
else { failed++; }
|
||||
}
|
||||
|
||||
var body = $"<h2>Backfill des pseudos Steam</h2><p class='muted'>{updated} mis à jour · {skipped} ignorés · {failed} échec(s)</p>" +
|
||||
(updated > 0 ? $"<table><thead><tr><th>PlayFabId</th><th>Nouveau nom</th></tr></thead><tbody>{rows}</tbody></table>" : "") +
|
||||
"<p><a class='btn' href='/admin/players'>← Joueurs</a></p>";
|
||||
return Html(Layout("Backfill", body));
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
|
||||
private bool IsLan()
|
||||
|
||||
@@ -32,6 +32,7 @@ public class ClientController : Controller
|
||||
private readonly DbEntityService _entityService;
|
||||
private readonly UserDataService _userDataService;
|
||||
private readonly TitleDataService _titleDataService;
|
||||
private readonly Prospect.Server.Api.Services.Steam.SteamWebApiService _steamWebApi;
|
||||
|
||||
public ClientController(ILogger<ClientController> logger,
|
||||
IOptions<PlayFabSettings> settings,
|
||||
@@ -39,7 +40,8 @@ public class ClientController : Controller
|
||||
DbUserService userService,
|
||||
DbEntityService entityService,
|
||||
UserDataService userDataService,
|
||||
TitleDataService titleDataService)
|
||||
TitleDataService titleDataService,
|
||||
Prospect.Server.Api.Services.Steam.SteamWebApiService steamWebApi)
|
||||
{
|
||||
_settings = settings.Value;
|
||||
_logger = logger;
|
||||
@@ -48,6 +50,7 @@ public class ClientController : Controller
|
||||
_entityService = entityService;
|
||||
_userDataService = userDataService;
|
||||
_titleDataService = titleDataService;
|
||||
_steamWebApi = steamWebApi;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
@@ -63,6 +66,19 @@ public class ClientController : Controller
|
||||
var userSteamId = ticket.SteamId.ToString();
|
||||
|
||||
var user = await _userService.FindOrCreateAsync(PlayFabUserAuthType.Steam, userSteamId);
|
||||
|
||||
// Backfill the display name from the Steam persona when we don't have a real
|
||||
// one yet (accounts are created as "Unknown"; the client never sends a name).
|
||||
if (string.IsNullOrEmpty(user.DisplayName) || user.DisplayName == "Unknown")
|
||||
{
|
||||
var persona = await _steamWebApi.GetPersonaNameAsync(userSteamId);
|
||||
if (!string.IsNullOrWhiteSpace(persona))
|
||||
{
|
||||
await _userService.UpdateDisplayNameAsync(user.Id, persona);
|
||||
user.DisplayName = persona;
|
||||
}
|
||||
}
|
||||
|
||||
var entity = await _entityService.FindOrCreateAsync(user.Id);
|
||||
|
||||
var userTicket = _authTokenService.GenerateUser(entity);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Prospect.Server.Api.Services.Steam;
|
||||
|
||||
// Resolves a Steam persona (display) name from a SteamID64 via the Steam Web API.
|
||||
// PlayFab does this server-side too; our emulator only gets the SteamID from the auth
|
||||
// ticket, so the real name needs an API key. Inert (no-op) when no key is configured.
|
||||
public class SteamWebApiService
|
||||
{
|
||||
private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromSeconds(5) };
|
||||
|
||||
private readonly string? _apiKey;
|
||||
private readonly ILogger<SteamWebApiService> _logger;
|
||||
|
||||
public SteamWebApiService(IConfiguration configuration, ILogger<SteamWebApiService> logger)
|
||||
{
|
||||
_apiKey = configuration["SteamWebApiKey"];
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool Enabled => !string.IsNullOrWhiteSpace(_apiKey);
|
||||
|
||||
public async Task<string?> GetPersonaNameAsync(string steamId64)
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var url = $"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key={_apiKey}&steamids={steamId64}";
|
||||
var json = await Http.GetStringAsync(url);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
if (doc.RootElement.TryGetProperty("response", out var response) &&
|
||||
response.TryGetProperty("players", out var players) &&
|
||||
players.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var player in players.EnumerateArray())
|
||||
{
|
||||
if (player.TryGetProperty("personaname", out var name))
|
||||
{
|
||||
return name.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Steam GetPlayerSummaries failed for {SteamId}", steamId64);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@ public class Startup
|
||||
services.AddSingleton<UserDataService>();
|
||||
services.AddSingleton<TitleDataService>();
|
||||
|
||||
services.AddSingleton<Prospect.Server.Api.Services.Steam.SteamWebApiService>();
|
||||
|
||||
services.AddSingleton<DbUserService>();
|
||||
services.AddSingleton<DbEntityService>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user