feat(lolstats): add data renewal — auto-refresh stale op.gg data
Uses op.gg REST API to trigger summoner data renewal before fetching stats via MCP. Adds Update button in profile header for manual refresh. Flow: lookup summoner_id → POST renewal → poll until finish → re-fetch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
40c596fbfa
commit
f4c8cce2f9
4 changed files with 218 additions and 3 deletions
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* op.gg MCP Client — calls the public op.gg MCP server at https://mcp-api.op.gg/mcp
|
||||
* Uses JSON-RPC 2.0 over StreamableHTTP (MCP protocol).
|
||||
* Also uses op.gg REST API for summoner renewal (data refresh).
|
||||
* No API key needed.
|
||||
*/
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ import type {
|
|||
} from './types.js';
|
||||
|
||||
const MCP_URL = 'https://mcp-api.op.gg/mcp';
|
||||
const OPGG_API = 'https://lol-api-summoner.op.gg/api';
|
||||
let rpcId = 1;
|
||||
|
||||
// ── Regions ──
|
||||
|
|
@ -334,3 +336,108 @@ export async function getMatchDetail(
|
|||
const parsed = parseClassNotation(raw);
|
||||
return parsed?.game_detail ?? parsed?.data?.game_detail ?? null;
|
||||
}
|
||||
|
||||
// ── Summoner Renewal (op.gg REST API) ──
|
||||
// Triggers a data refresh on op.gg's servers so the MCP returns fresh data.
|
||||
|
||||
/**
|
||||
* Look up the op.gg-internal summoner_id for a Riot ID.
|
||||
* Required for the renewal endpoint.
|
||||
*/
|
||||
async function getSummonerId(
|
||||
gameName: string, tagLine: string, region: string,
|
||||
): Promise<string> {
|
||||
const regionLower = region.toLowerCase();
|
||||
const riotId = `${gameName}-${tagLine}`;
|
||||
const url = `${OPGG_API}/v3/${regionLower}/summoners?riot_id=${encodeURIComponent(riotId)}&hl=en_US`;
|
||||
|
||||
console.log(`[LoLStats] Looking up summoner_id: ${url}`);
|
||||
|
||||
const res = await fetch(url, {
|
||||
headers: { 'Accept': 'application/json' },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`op.gg summoner lookup HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
const json = await res.json() as any;
|
||||
const summonerId = json?.data?.summoner_id ?? json?.data?.id ?? json?.summoner_id;
|
||||
|
||||
if (!summonerId) {
|
||||
// Try to find it in a different structure
|
||||
const alt = json?.data?.[0]?.summoner_id ?? json?.[0]?.summoner_id;
|
||||
if (alt) return String(alt);
|
||||
console.log('[LoLStats] Summoner lookup response:', JSON.stringify(json).slice(0, 500));
|
||||
throw new Error('Could not find summoner_id in op.gg response');
|
||||
}
|
||||
|
||||
return String(summonerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a data renewal on op.gg and poll until complete.
|
||||
* Returns the last_updated_at timestamp on success.
|
||||
*/
|
||||
export async function renewSummoner(
|
||||
gameName: string, tagLine: string, region: string,
|
||||
): Promise<{ renewed: boolean; last_updated_at: string; message?: string }> {
|
||||
const regionLower = region.toLowerCase();
|
||||
|
||||
// Step 1: Get summoner_id
|
||||
let summonerId: string;
|
||||
try {
|
||||
summonerId = await getSummonerId(gameName, tagLine, region);
|
||||
} catch (e: any) {
|
||||
console.error('[LoLStats] Summoner lookup failed:', e.message);
|
||||
throw new Error(`Summoner not found on op.gg: ${e.message}`);
|
||||
}
|
||||
|
||||
console.log(`[LoLStats] Triggering renewal for summoner_id=${summonerId} region=${regionLower}`);
|
||||
|
||||
// Step 2: POST renewal
|
||||
const renewUrl = `${OPGG_API}/${regionLower}/summoners/${encodeURIComponent(summonerId)}/renewal`;
|
||||
const maxPolls = 10;
|
||||
|
||||
for (let attempt = 0; attempt < maxPolls; attempt++) {
|
||||
const res = await fetch(renewUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
});
|
||||
|
||||
const json = await res.json() as any;
|
||||
|
||||
// Cooldown (already renewed recently) — status 200 but with message
|
||||
if (json.message?.includes('Already renewed') || json.message?.includes('already')) {
|
||||
console.log(`[LoLStats] Already renewed recently, last_updated_at=${json.last_updated_at}`);
|
||||
return {
|
||||
renewed: true,
|
||||
last_updated_at: json.last_updated_at ?? '',
|
||||
message: json.message,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if finish
|
||||
const data = json.data ?? json;
|
||||
if (data.finish === true) {
|
||||
console.log(`[LoLStats] Renewal complete, last_updated_at=${data.last_updated_at}`);
|
||||
return {
|
||||
renewed: true,
|
||||
last_updated_at: data.last_updated_at ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
// Not finished — wait and poll again
|
||||
const delay = Math.min(data.delay ?? 1000, 3000);
|
||||
console.log(`[LoLStats] Renewal in progress, waiting ${delay}ms (attempt ${attempt + 1}/${maxPolls})`);
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
}
|
||||
|
||||
// Timeout — renewal took too long
|
||||
console.warn('[LoLStats] Renewal polling timeout');
|
||||
return {
|
||||
renewed: false,
|
||||
last_updated_at: '',
|
||||
message: 'Renewal timed out — data may still be updating',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue