Spaces:
Runtime error
Runtime error
| import type { MatchContextView, PreMatchTeamStats, WcPrediction } from "@/domain/entities"; | |
| function parsePercent(text: string): number | undefined { | |
| const m = text.match(/([\d.]+)\s*%/); | |
| return m ? parseFloat(m[1]) / 100 : undefined; | |
| } | |
| function parseTeamBlock(body: string): PreMatchTeamStats | null { | |
| const elo = body.match(/Elo:\s*([\d.]+)/i); | |
| const goals = body.match(/Gols\/jogo:\s*([\d.]+)/i); | |
| const conceded = body.match(/Sofridos\/jogo:\s*([\d.]+)/i); | |
| const form = body.match(/Forma recente:\s*(.+)/i); | |
| if (!elo) return null; | |
| return { | |
| elo: parseFloat(elo[1]), | |
| goalsPerGame: goals ? parseFloat(goals[1]) : 0, | |
| concededPerGame: conceded ? parseFloat(conceded[1]) : 0, | |
| form: form?.[1]?.trim() ?? "—", | |
| }; | |
| } | |
| function parseKxlTeam(body: string) { | |
| const attack = body.match(/Ataque:\s*([\d.]+)/i); | |
| const defense = body.match(/Defesa:\s*([\d.]+)/i); | |
| const control = body.match(/Controle:\s*([\d.]+)/i); | |
| if (!attack) return null; | |
| return { | |
| attack: parseFloat(attack[1]), | |
| defense: defense ? parseFloat(defense[1]) : 0, | |
| control: control ? parseFloat(control[1]) : 0, | |
| shots: (() => { | |
| const m = body.match(/Chutes\/jogo:\s*([\d.]+)/i); | |
| return m ? parseFloat(m[1]) : undefined; | |
| })(), | |
| possession: (() => { | |
| const m = body.match(/Posse:\s*([\d.]+%?)/i); | |
| return m ? parsePercent(m[1]) : undefined; | |
| })(), | |
| counter: (() => { | |
| const m = body.match(/Contra-ataque:\s*([\d.]+)/i); | |
| return m ? parseFloat(m[1]) : undefined; | |
| })(), | |
| gkWeakness: (() => { | |
| const m = body.match(/Fraqueza GK dentro da área:\s*([\d.]+%?)/i); | |
| return m ? parsePercent(m[1]) : undefined; | |
| })(), | |
| }; | |
| } | |
| export function parseMatchContextFromMarkdown( | |
| context: string, | |
| homeTeam: string, | |
| awayTeam: string, | |
| ): MatchContextView { | |
| const sections = context.split(/^## /m).filter(Boolean); | |
| let preMatch: MatchContextView["preMatch"] = null; | |
| let kxlProfile: MatchContextView["kxlProfile"] = null; | |
| for (const raw of sections) { | |
| const titleLine = raw.split("\n")[0]?.trim() ?? ""; | |
| const body = raw.slice(raw.indexOf("\n") + 1); | |
| if (titleLine.includes("Estatísticas pré-jogo")) { | |
| const teams = [...body.matchAll(/^### (.+)$/gm)]; | |
| const h2h = body.match( | |
| /Vitórias ([^:]+):\s*(\d+).*Empates:\s*(\d+).*Vitórias ([^:]+):\s*(\d+)/, | |
| ); | |
| const homeBlock = teams.find((t) => t[1].trim() === homeTeam); | |
| const awayBlock = teams.find((t) => t[1].trim() === awayTeam); | |
| if (homeBlock && awayBlock) { | |
| const homeStart = homeBlock.index!; | |
| const awayStart = awayBlock.index!; | |
| const homeBody = body.slice(homeStart, awayStart); | |
| const awayBody = body.slice(awayStart); | |
| const homeStats = parseTeamBlock(homeBody); | |
| const awayStats = parseTeamBlock(awayBody); | |
| if (homeStats && awayStats) { | |
| preMatch = { | |
| home: homeStats, | |
| away: awayStats, | |
| h2h: { | |
| homeWins: h2h ? parseInt(h2h[2], 10) : 0, | |
| draws: h2h ? parseInt(h2h[3], 10) : 0, | |
| awayWins: h2h ? parseInt(h2h[5], 10) : 0, | |
| total: h2h | |
| ? parseInt(h2h[2], 10) + parseInt(h2h[3], 10) + parseInt(h2h[5], 10) | |
| : 0, | |
| }, | |
| }; | |
| } | |
| } | |
| } | |
| if (titleLine.includes("Perfil tático KXL")) { | |
| const teams = [...body.matchAll(/^### (.+)$/gm)]; | |
| const homeBlock = teams.find((t) => t[1].trim() === homeTeam); | |
| const awayBlock = teams.find((t) => t[1].trim() === awayTeam); | |
| const sectorNote = body.match(/^- (.+vantagem.+)$/m)?.[1]; | |
| const pressure = body.match( | |
| /Pressão ofensiva .+?:\s*([\d.]+)\s*\|\s*.+?:\s*([\d.]+)/, | |
| ); | |
| const probs = body.match( | |
| /Palpite vetorial[^:]*:\s*1=(\d+)%\s*X=(\d+)%\s*2=(\d+)%/, | |
| ); | |
| if (homeBlock && awayBlock) { | |
| const homeStart = homeBlock.index!; | |
| const awayStart = awayBlock.index!; | |
| const homeBody = body.slice(homeStart, awayStart); | |
| const awayBody = body.slice(awayStart, body.indexOf("### Matchup")); | |
| const homeKxl = parseKxlTeam(homeBody); | |
| const awayKxl = parseKxlTeam(awayBody); | |
| if (homeKxl && awayKxl) { | |
| kxlProfile = { | |
| home: homeKxl, | |
| away: awayKxl, | |
| sectorNote: sectorNote ?? "", | |
| pressureHome: pressure ? parseFloat(pressure[1]) : 0, | |
| pressureAway: pressure ? parseFloat(pressure[2]) : 0, | |
| probHome: probs ? parseInt(probs[1], 10) / 100 : 0, | |
| probDraw: probs ? parseInt(probs[2], 10) / 100 : 0, | |
| probAway: probs ? parseInt(probs[3], 10) / 100 : 0, | |
| }; | |
| } | |
| } | |
| } | |
| } | |
| return { preMatch, kxlProfile }; | |
| } | |
| export function buildMatchContextView(prediction: WcPrediction): MatchContextView { | |
| return parseMatchContextFromMarkdown( | |
| prediction.context, | |
| prediction.homeTeam, | |
| prediction.awayTeam, | |
| ); | |
| } | |