@@ -20,100 +20,53 @@ import {
@Injectable ( )
export class MatchesService {
private readonly logger = new Logger ( MatchesService . name ) ;
private qualifiedLeagueIds : string [ ] = [ ] ;
private topLeagueIds : string [ ] = [ ] ;
constructor ( private readonly prisma : PrismaService ) {
this . loadQualifiedLeagues ( ) ;
this . loadTopLeagues ( ) ;
}
private loadTopLeagues() {
try {
const topLeaguesPath = path . join ( process . cwd ( ) , "top_leagues.json" ) ;
if ( fs . existsSync ( topLeaguesPath ) ) {
this . topLeagueIds = JSON . parse ( fs . readFileSync ( topLeaguesPath , "utf8" ) ) ;
this . logger . log (
` Loaded ${ this . topLeagueIds . length } top leagues for filtering. ` ,
) ;
const filePath = path . join ( process . cwd ( ) , "top_leagues.json" ) ;
if ( fs . existsSync ( filePath ) ) {
this . topLeagueIds = JSON . parse ( fs . readFileSync ( filePath , "utf8" ) ) ;
}
} catch ( e ) {
this . logger . warn ( ` Failed to load top_leagues.json: ${ e . message } ` ) ;
}
}
/**
* Generate flag URL from country code or name using flagcdn.com
* Falls back to a name-to-code mapping for Turkish country names
*/
private getCountryFlagUrl (
countryCode? : string | null ,
countryName? : string | null ,
) : string | undefined {
// If we have a 2-letter ISO code, use it directly
if ( countryCode && countryCode . length === 2 ) {
return ` https://flagcdn.com/w40/ ${ countryCode . toLowerCase ( ) } .png ` ;
}
// Fallback: map common Turkish country names to ISO codes
const COUNTRY_NAME_TO_CODE : Record < string , string > = {
"Türkiye" : "tr" ,
"İngiltere" : "gb-eng" ,
"İspanya" : "es" ,
"İtalya" : "it" ,
"Almanya" : "de" ,
"Fransa" : "fr" ,
"Portekiz" : "pt" ,
"Hollanda" : "nl" ,
"Belçika" : "be" ,
"İskoçya" : "gb-sct" ,
"Galler" : "gb-wls" ,
"İrlanda" : "ie" ,
"Avusturya" : "at" ,
"İsviçre" : "ch" ,
"Yunanistan" : "gr" ,
"Polonya" : "pl" ,
"Çekya" : "cz" ,
"Hı rvatistan" : "hr" ,
"Sı rbistan" : "rs" ,
"Danimarka" : "dk" ,
"Norveç" : "no" ,
"İsveç" : "se" ,
"Finlandiya" : "fi" ,
"Rusya" : "ru" ,
"Ukrayna" : "ua" ,
"Romanya" : "ro" ,
"Macaristan" : "hu" ,
"Bulgaristan" : "bg" ,
"Arjantin" : "ar" ,
"Brezilya" : "br" ,
"Meksika" : "mx" ,
"ABD" : "us" ,
"Japonya" : "jp" ,
"Güney Kore" : "kr" ,
"Çin" : "cn" ,
"Avustralya" : "au" ,
"Suudi Arabistan" : "sa" ,
"BAE" : "ae" ,
"Katar" : "qa" ,
"Mı sı r" : "eg" ,
"Güney Afrika" : "za" ,
"Kolombiya" : "co" ,
"Şili" : "cl" ,
"Peru" : "pe" ,
"Ekvador" : "ec" ,
"Paraguay" : "py" ,
"Uruguay" : "uy" ,
"Avrupa" : "eu" ,
"Dünya" : "un" ,
} ;
if ( countryName ) {
const code = COUNTRY_NAME_TO_CODE [ countryName ] ;
if ( code ) {
return ` https://flagcdn.com/w40/ ${ code } .png ` ;
private loadQualifiedLeagues() {
try {
const filePath = path . join ( process . cwd ( ) , "qualified_leagues.json" ) ;
if ( fs . existsSync ( filePath ) ) {
this . qualifiedLeagueIds = JSON . parse ( fs . readFileSync ( filePath , "utf8" ) ) ;
this . logger . log (
` Loaded ${ this . qualifiedLeagueIds . length } qualified leagues for filtering. ` ,
) ;
}
} catch ( e ) {
this . logger . warn ( ` Failed to load qualified_leagues.json: ${ e . message } ` ) ;
}
}
return undefined ;
/**
* Generate URL for the country flag served from Mackolik
*/
private getCountryFlagUrl ( countryId? : string | null ) : string | undefined {
if ( ! countryId ) return undefined ;
return ` https://file.mackolikfeeds.com/areas/ ${ countryId } ` ;
}
/**
* Generate URL for the team logo served from local uploads
*/
private getTeamLogoUrl ( teamId? : string | null ) : string | undefined {
if ( ! teamId ) return undefined ;
return ` https://file.mackolikfeeds.com/teams/ ${ teamId } ` ;
}
private getLiveFilter ( ) : Prisma . LiveMatchWhereInput {
@@ -215,10 +168,9 @@ export class MatchesService {
if ( leagueId ) {
where . leagueId = leagueId ;
} else if ( this . topLeagueIds . length > 0 ) {
// Always filter by top leagues when no specific leagueId is requested
// This ensures match list is consistent with the active leagues sidebar
where . leagueId = { in : this . topLeagueIds } ;
} else if ( this . qualifiedLeagueIds . length > 0 ) {
// Only show matches from qualified leagues (leagues with historical data for AI analysis)
where . leagueId = { in : this . qualifiedLeagueIds } ;
}
if ( status === "LIVE" ) {
@@ -375,7 +327,9 @@ export class MatchesService {
country : {
id : match.league?.country?.id || "" ,
name : match.league?.country?.name || "" ,
flagUrl : match.league?.country?.flagUrl || this . getCountryFlagUrl ( null , match . league ? . country ? . name ) ,
flagUrl :
match.league?.country?.flagUrl ||
this . getCountryFlagUrl ( match . league ? . country ? . id ) ,
} ,
sport : sport ,
matches : [ ] ,
@@ -430,11 +384,11 @@ export class MatchesService {
htScoreAway : undefined ,
homeTeamName : match.homeTeam?.name || "Unknown" ,
homeTeamLogo : match.homeTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ match . homeTeamId } `
? this . getTeamLogoUrl ( match . homeTeamId )
: undefined ,
awayTeamName : match.awayTeam?.name || "Unknown" ,
awayTeamLogo : match.awayTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ match . awayTeamId } `
? this . getTeamLogoUrl ( match . awayTeamId )
: undefined ,
leagueName : match.league?.name ,
countryName : match.league?.country?.name ,
@@ -442,7 +396,15 @@ export class MatchesService {
} ) ;
}
return Array . from ( leaguesMap . values ( ) ) ;
return Array . from ( leaguesMap . values ( ) ) . sort ( ( a , b ) = > {
const aIdx = this . topLeagueIds . indexOf ( a . id ) ;
const bIdx = this . topLeagueIds . indexOf ( b . id ) ;
const aPriority = aIdx === - 1 ? 999 : aIdx ;
const bPriority = bIdx === - 1 ? 999 : bIdx ;
if ( aPriority !== bPriority ) return aPriority - bPriority ;
return ( a . name || "" ) . localeCompare ( b . name || "" ) ;
} ) ;
}
/**
@@ -465,6 +427,7 @@ export class MatchesService {
const leagues = await this . prisma . $queryRaw < any [ ] > `
SELECT
l.id, l.name, l.code,
c.id as country_id,
c.name as country_name,
c.flag_url as country_flag,
COUNT(lm.id)::int as match_count,
@@ -474,34 +437,21 @@ export class MatchesService {
JOIN leagues l ON lm.league_id = l.id
LEFT JOIN countries c ON l.country_id = c.id
WHERE lm.sport = ${ sport }
${ this . topLeagueIds . length > 0 ? Prisma . sql ` AND l.id IN ( ${ Prisma . join ( this . topLeagueIds ) } ) ` : Prisma . empty }
${ this . qualifiedLeagueIds . length > 0 ? Prisma . sql ` AND l.id IN ( ${ Prisma . join ( this . qualifiedLeagueIds ) } ) ` : Prisma . empty }
AND (
(lm.mst_utc >= ${ todayMs } AND lm.status NOT IN ( ${ Prisma . join ( finishedStatuses ) } ) AND COALESCE(lm.state, '') NOT IN ( ${ Prisma . join ( finishedStates ) } ))
OR lm.status IN ( ${ Prisma . join ( liveStatuses ) } )
OR lm.state IN ( ${ Prisma . join ( liveStates ) } )
)
GROUP BY l.id, l.name, l.code, c.name, c.flag_url
GROUP BY l.id, l.name, l.code, c.id, c.name, c.flag_url
ORDER BY l.name ASC
` ;
// Priority sorting (Mackolik style)
const PRIORITY = [
"Trendyol Süper Lig" ,
"Süper Lig" ,
"Trendyol 1. Lig" ,
"1. Lig" ,
"Premier Lig" ,
"LaLiga" ,
"Serie A" ,
"Bundesliga" ,
"Ligue 1" ,
] ;
return leagues
. filter ( ( l ) = > l . match_count > 0 )
. sort ( ( a , b ) = > {
const aIdx = PRIORITY . findIndex ( ( p ) = > a . name ? . includes ( p ) ) ;
const bIdx = PRIORITY . findIndex ( ( p ) = > b . name ? . includes ( p ) ) ;
const aIdx = this . topLeagueIds . indexOf ( a . id ) ;
const bIdx = this . topLeagueIds . indexOf ( b . id ) ;
const aPriority = aIdx === - 1 ? 999 : aIdx ;
const bPriority = bIdx === - 1 ? 999 : bIdx ;
@@ -514,7 +464,7 @@ export class MatchesService {
name : l.name ,
code : l.code ,
countryName : l.country_name ,
countryFlag : l.country_flag || this . getCountryFlagUrl ( null , l . country_name ) ,
countryFlag : l.country_flag || this . getCountryFlagUrl ( l . country_id ) ,
matchCount : l.match_count ,
liveCount : l.live_count ,
} ) ) ;
@@ -553,13 +503,9 @@ export class MatchesService {
scoreAway : m.scoreAway ,
status : m.status ,
homeTeamName : m.homeTeam?.name ,
homeTeamLogo : m.homeTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ m . homeTeamId } `
: null ,
homeTeamLogo : m.homeTeamId ? this . getTeamLogoUrl ( m . homeTeamId ) : null ,
awayTeamName : m.awayTeam?.name ,
awayTeamLogo : m.awayTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ m . awayTeamId } `
: null ,
awayTeamLogo : m.awayTeamId ? this . getTeamLogoUrl ( m . awayTeamId ) : null ,
leagueName : m.league?.name ,
countryName : m.league?.country?.name ,
} ) ) ,
@@ -860,13 +806,13 @@ export class MatchesService {
homeTeam : {
. . . match . homeTeam ,
logo : match.homeTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ match . homeTeamId } `
? this . getTeamLogoUrl ( match . homeTeamId )
: match . homeTeam ? . logoUrl || null ,
} ,
awayTeam : {
. . . match . awayTeam ,
logo : match.awayTeamId
? ` https://file.mackolikfeeds.com/teams/ ${ match . awayTeamId } `
? this . getTeamLogoUrl ( match . awayTeamId )
: match . awayTeam ? . logoUrl || null ,
} ,
stats : {