package org.example; import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.example.models.Club; import org.example.models.TeamStanding; import org.jsoup.Connection; import org.jsoup.Jsoup; import java.io.FileInputStream; import java.util.*; public class StandingsScraper { public static void main(String[] args) { String caminhoChave = "service-account.json"; String urlDatabase = "https://vdcscore-default-rtdb.firebaseio.com/"; try { System.out.println("--- A CONECTAR AO FIREBASE ---"); if (FirebaseApp.getApps().isEmpty()) { FileInputStream serviceAccount = new FileInputStream(caminhoChave); FirebaseOptions options = FirebaseOptions.builder() .setCredentials(GoogleCredentials.fromStream(serviceAccount)) .setDatabaseUrl(urlDatabase) .build(); FirebaseApp.initializeApp(options); } } catch (Exception e) { System.err.println("ERRO FIREBASE: Verifica o service-account.json!"); e.printStackTrace(); return; } Gson gson = new Gson(); Map clubesMap = new HashMap<>(); // 1. Obter todos os Clubes e seus meta-dados System.out.println("\n--- A EXTRAIR DADOS DOS CLUBES ---"); try { String urlTodosClubes = "https://api.afavcd.pt/teams"; String jsonTodosClubes = Jsoup.connect(urlTodosClubes) .ignoreContentType(true) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)") .header("Referer", "https://www.afavcd.pt/") .header("Origin", "https://www.afavcd.pt") .method(Connection.Method.GET) .execute() .body(); List> dadosClubes = gson.fromJson(jsonTodosClubes, new TypeToken>>() { }.getType()); for (Map c : dadosClubes) { if (c.get("teamID") != null) { int clubId = (int) Double.parseDouble(c.get("teamID").toString()); Club club = new Club(); club.setId(clubId); if (c.get("name") != null) club.setName(c.get("name").toString()); if (c.get("logoURL") != null) club.setImageUrl(c.get("logoURL").toString()); clubesMap.put(clubId, club); } } System.out.println("Encontrados " + clubesMap.size() + " clubes."); } catch (Exception e) { System.err.println("Erro ao obter lista de clubes."); e.printStackTrace(); return; } int[] modalidades = { 3, 1 }; String[] nomesEscaloes = { "juniores", "seniores" }; for (int i = 0; i < modalidades.length; i++) { int modalidade = modalidades[i]; String escalao = nomesEscaloes[i]; System.out.println("\n========================================"); System.out.println("A CALCULAR CLASSIFICAÇÃO PARA: " + escalao.toUpperCase()); System.out.println("========================================"); String urlJornadas = "https://api.afavcd.pt/seasons/33/teams/modality/" + modalidade + "/jorneys"; try { String jsonJornadas = Jsoup.connect(urlJornadas) .ignoreContentType(true) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)") .header("Referer", "https://www.afavcd.pt/") .header("Origin", "https://www.afavcd.pt") .method(Connection.Method.GET) .execute() .body(); Map>> matchdays = gson.fromJson(jsonJornadas, new TypeToken>>>() { }.getType()); Map standingsMap = new HashMap<>(); for (List> matchday : matchdays.values()) { for (Map match : matchday) { try { int homeTeamId = parseId(match.get("Home")); int awayTeamId = parseId(match.get("Away")); // Ignorar jogos com equipas placeholder / de descanso if (homeTeamId < 0 || awayTeamId < 0) continue; String homeGoalsStr = String.valueOf(match.get("homeGoals")); String awayGoalsStr = String.valueOf(match.get("awayGoals")); if (homeGoalsStr != null && !homeGoalsStr.trim().isEmpty() && !homeGoalsStr.equals("null")) { int homeGoals = (int) Double.parseDouble(homeGoalsStr); int awayGoals = (int) Double.parseDouble(awayGoalsStr); // Inicializar as equipas caso não existam no standingsMap standingsMap.putIfAbsent(homeTeamId, createInitialStanding(homeTeamId, clubesMap)); standingsMap.putIfAbsent(awayTeamId, createInitialStanding(awayTeamId, clubesMap)); standingsMap.get(homeTeamId).addMatchResult(homeGoals, awayGoals); standingsMap.get(awayTeamId).addMatchResult(awayGoals, homeGoals); } } catch (Exception ex) { // Ignorar erro em um jogo específico (dados incorretos, etc) } } } // Transformar em lista e ordenar List sortedStandings = new ArrayList<>(standingsMap.values()); Collections.sort(sortedStandings); System.out.println("Classificação Final " + escalao.toUpperCase() + ":"); int posicao = 1; for (TeamStanding ts : sortedStandings) { ts.setPosition(posicao); System.out.printf("%d. %s - %d pts (J:%d, V:%d, E:%d, D:%d, GM:%d, GS:%d)\n", posicao, ts.getName(), ts.getPoints(), ts.getMatchesPlayed(), ts.getWins(), ts.getDraws(), ts.getLosses(), ts.getGoalsFor(), ts.getGoalsAgainst()); posicao++; } // Enviar para Firebase DatabaseReference refClassificacoes = FirebaseDatabase.getInstance() .getReference("classificacoes").child(escalao); refClassificacoes.setValueAsync(sortedStandings); System.out.println("-> Classificação de " + escalao + " enviada para o Firebase."); } catch (Exception e) { System.err.println("Erro a processar " + escalao); e.printStackTrace(); } } try { System.out.println("\nAguardando confirmação do servidor (5s)..."); Thread.sleep(5000); System.out.println("Processo concluído!"); System.exit(0); } catch (InterruptedException e) { e.printStackTrace(); } } private static int parseId(Object value) { if (value == null) return -1; try { return (int) Double.parseDouble(value.toString()); } catch (NumberFormatException e) { return -1; } } private static TeamStanding createInitialStanding(int teamId, Map clubesMap) { Club club = clubesMap.get(teamId); if (club != null) { return new TeamStanding(teamId, club.getName(), club.getImageUrl()); } return new TeamStanding(teamId, "Equipa " + teamId, ""); } }