amarorn / frontend /src /presentation /utils /parseMatchContext.ts
beAnalytic's picture
feat: sync main with feature/superbet-live-inplay
16c19b8 verified
Raw
History Blame Contribute Delete
4.98 kB
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,
);
}