125 lines
3.2 KiB
TypeScript
125 lines
3.2 KiB
TypeScript
import { Injectable, Logger } from "@nestjs/common";
|
|
import axios from "axios";
|
|
|
|
/**
|
|
* Spor Toto API response types
|
|
* Source: https://sportotov2.iddaa.com/SporToto
|
|
*/
|
|
export interface SporTotoApiEvent {
|
|
eventNo: number;
|
|
eventName: string; // "Blackpool-Burton Albion"
|
|
competitionName: string; // "İN1"
|
|
eventDate: string; // "2026-03-28T18:00:00"
|
|
result: string | null;
|
|
winner: string | null;
|
|
}
|
|
|
|
export interface SporTotoApiDividend {
|
|
winnerCount15?: number;
|
|
dividend15?: number;
|
|
winnerCount14?: number;
|
|
dividend14?: number;
|
|
winnerCount13?: number;
|
|
dividend13?: number;
|
|
winnerCount12?: number;
|
|
dividend12?: number;
|
|
}
|
|
|
|
export interface SporTotoApiResponse {
|
|
isSuccess: boolean;
|
|
data: {
|
|
payinBeginDate: string;
|
|
payinEndDate: string;
|
|
gameCycleNo: number;
|
|
dividends: SporTotoApiDividend | null;
|
|
events: SporTotoApiEvent[];
|
|
programName: string;
|
|
nextDrawExpectedWins: number | null;
|
|
} | null;
|
|
message: string;
|
|
error: string | null;
|
|
info: string | null;
|
|
dateTime: string | null;
|
|
}
|
|
|
|
@Injectable()
|
|
export class TotoFetcherService {
|
|
private readonly logger = new Logger(TotoFetcherService.name);
|
|
private readonly apiUrl = "https://sportotov2.iddaa.com/SporToto";
|
|
|
|
/**
|
|
* Fetch current bulletin from Spor Toto API
|
|
*/
|
|
async fetchCurrentBulletin(): Promise<SporTotoApiResponse | null> {
|
|
try {
|
|
this.logger.log("Fetching current Spor Toto bulletin...");
|
|
const response = await axios.get<SporTotoApiResponse>(this.apiUrl, {
|
|
timeout: 10000,
|
|
headers: {
|
|
Accept: "application/json",
|
|
"User-Agent": "SuggestBet/1.0",
|
|
},
|
|
});
|
|
|
|
if (!response.data?.isSuccess || !response.data?.data) {
|
|
this.logger.warn(
|
|
"Spor Toto API returned unsuccessful response",
|
|
response.data?.message,
|
|
);
|
|
return null;
|
|
}
|
|
|
|
this.logger.log(
|
|
`Fetched bulletin: Cycle ${response.data.data.gameCycleNo} — ${response.data.data.programName} (${response.data.data.events.length} events)`,
|
|
);
|
|
return response.data;
|
|
} catch (error) {
|
|
if (axios.isAxiosError(error)) {
|
|
this.logger.error(
|
|
`Spor Toto API error: ${error.message}`,
|
|
error.response?.status,
|
|
);
|
|
} else {
|
|
this.logger.error("Spor Toto fetch failed", error);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse "Blackpool-Burton Albion" → { home: "Blackpool", away: "Burton Albion" }
|
|
*/
|
|
parseEventName(eventName: string): {
|
|
homeTeam: string;
|
|
awayTeam: string;
|
|
} {
|
|
const parts = eventName.split("-");
|
|
if (parts.length >= 2) {
|
|
return {
|
|
homeTeam: parts[0].trim(),
|
|
awayTeam: parts.slice(1).join("-").trim(),
|
|
};
|
|
}
|
|
return { homeTeam: eventName, awayTeam: "" };
|
|
}
|
|
|
|
/**
|
|
* Map API result/winner to TotoMatchResult enum value
|
|
* API returns: "1" (HOME), "0" (DRAW), "2" (AWAY)
|
|
*/
|
|
mapResultToEnum(winner: string | null): "HOME" | "DRAW" | "AWAY" | null {
|
|
if (!winner) return null;
|
|
switch (winner) {
|
|
case "1":
|
|
return "HOME";
|
|
case "0":
|
|
case "X":
|
|
return "DRAW";
|
|
case "2":
|
|
return "AWAY";
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|