From c698008b0ca4a14e106bc13e32df539b2e17315f Mon Sep 17 00:00:00 2001 From: Julian Arndt Date: Mon, 27 Jul 2026 21:24:35 +0200 Subject: [PATCH] Cleanup code and prepare for AI usage --- .vscode/settings.json | 10 +- AGENTS.md | 91 ++++++++++ README.md | 136 ++++++++++++++ docs/apifootball.md | 39 ++++ docs/googlecalendar.md | 29 +++ docs/teamidmatcher.md | 25 +++ docs/tippliga.md | 99 +++++++++++ docs/tippligaforum.md | 48 +++++ docs/whatsapp.md | 40 +++++ jenkinsfiles/LigaCupUpdater/Jenkinsfile | 4 +- jenkinsfiles/RelegationUpdater/Jenkinsfile | 4 +- jenkinsfiles/SupercupUpdater/Jenkinsfile | 4 +- jenkinsfiles/TippligaUpdater/Jenkinsfile | 4 +- jenkinsfiles/WTLPokalUpdater/Jenkinsfile | 4 +- mise.toml | 2 + .../CalendarConfigProvider.java | 37 +--- .../GoogleCalendarConnector.java | 62 +++++-- .../openligadb/OpenLigaDBConnector.java | 142 --------------- .../de/jeyp91/openligadb/OpenLigaDBMatch.java | 88 --------- src/main/resources/Gist_Config.json | 4 - .../resources/Google_Calendar_Config.json | 6 - src/main/resources/Google_Credentials.json | 14 -- .../Tippliga_Configs/{ => 2023}/wtl_2023.json | 0 .../Tippliga_Configs/{ => 2024}/wtl_2024.json | 0 .../Tippliga_Configs/{ => 2025}/tl_2025.json | 0 .../{ => 2025}/tl_2025_v3.json | 0 .../Tippliga_Configs/{ => 2025}/wtl_2025.json | 0 .../{ => 2025}/wtl_2025_v3.json | 0 .../{lc_2026.json => 2026/lc.json} | 0 .../{tl_2026_v3.json => 2026/tl.json} | 0 .../{tl_2026_ligen.sql => 2026/tl_ligen.sql} | 0 .../{wtl_2026.json => 2026/wtl.json} | 0 .../resources/tippliga_config_template.json | 167 ------------------ .../java/de/jeyp91/TeamIDMatcherTest.java | 5 +- .../apifootball/APIFootballConnectorTest.java | 1 - 35 files changed, 579 insertions(+), 486 deletions(-) create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 docs/apifootball.md create mode 100644 docs/googlecalendar.md create mode 100644 docs/teamidmatcher.md create mode 100644 docs/tippliga.md create mode 100644 docs/tippligaforum.md create mode 100644 docs/whatsapp.md create mode 100644 mise.toml delete mode 100644 src/main/java/de/jeyp91/openligadb/OpenLigaDBConnector.java delete mode 100644 src/main/java/de/jeyp91/openligadb/OpenLigaDBMatch.java delete mode 100644 src/main/resources/Gist_Config.json delete mode 100644 src/main/resources/Google_Calendar_Config.json delete mode 100644 src/main/resources/Google_Credentials.json rename src/main/resources/Tippliga_Configs/{ => 2023}/wtl_2023.json (100%) rename src/main/resources/Tippliga_Configs/{ => 2024}/wtl_2024.json (100%) rename src/main/resources/Tippliga_Configs/{ => 2025}/tl_2025.json (100%) rename src/main/resources/Tippliga_Configs/{ => 2025}/tl_2025_v3.json (100%) rename src/main/resources/Tippliga_Configs/{ => 2025}/wtl_2025.json (100%) rename src/main/resources/Tippliga_Configs/{ => 2025}/wtl_2025_v3.json (100%) rename src/main/resources/Tippliga_Configs/{lc_2026.json => 2026/lc.json} (100%) rename src/main/resources/Tippliga_Configs/{tl_2026_v3.json => 2026/tl.json} (100%) rename src/main/resources/Tippliga_Configs/{tl_2026_ligen.sql => 2026/tl_ligen.sql} (100%) rename src/main/resources/Tippliga_Configs/{wtl_2026.json => 2026/wtl.json} (100%) delete mode 100644 src/main/resources/tippliga_config_template.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 63000db..61960bd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,15 +2,7 @@ "java.compile.nullAnalysis.mode": "automatic", "java.configuration.updateBuildConfiguration": "interactive", "java.test.config": { - "envFile": "${workspaceFolder}/.env", - "env": { - "AWS_ACCESS_KEY_ID": "AKIA4G4WGB26FLCFCU4M", - "AWS_SECRET_ACCESS_KEY": "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", - "FORUM_PASSWORD": "original", - "FORUM_USERNAME": "Julian", - "TLW_DATABASE_PASSWORD": "TippligaWuerzb_1", - "TLW_DATABASE_USERNAME": "d0144ddb" - } + "envFile": "${workspaceFolder}/.env" }, "java.debug.settings.onBuildFailureProceed": true, } diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ecd3d32 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,91 @@ + +# RTK (Rust Token Killer) - Token-Optimized Commands + +When running shell commands, **always prefix with `rtk`**. This reduces context +usage by 60-90% with zero behavior change. If rtk has no filter for a command, +it passes through unchanged — so it is always safe to use. + +## Key Commands +```bash +# Git (59-80% savings) +rtk git status rtk git diff rtk git log + +# Files & Search (60-75% savings) +rtk ls rtk read rtk grep +rtk find rtk diff + +# Build (80-90% savings) — shows errors only +rtk ./gradlew build rtk ./gradlew compileJava + +# Test (90-99% savings) — shows failures only +rtk ./gradlew test + +# Analysis (70-90% savings) +rtk err rtk log rtk json +rtk summary rtk deps rtk env + +# GitHub (26-87% savings) +rtk gh pr view rtk gh run list rtk gh issue list + +# Infrastructure (85% savings) +rtk docker ps rtk kubectl get rtk docker logs +``` + +## Rules +- In command chains, prefix each segment: `rtk git add . && rtk git commit -m "msg"` +- For debugging, use raw command without rtk prefix +- `rtk proxy ` runs command without filtering but tracks usage + + +--- + +# TLW Database Tool — Agent Guide + +## Project Overview +Java CLI tool managing the Tippliga Würzburg football prediction league. Syncs match data from API-Football, manages MySQL database (phpBB forum), pushes Google Calendar events, sends WhatsApp reminders via OpenAI-generated messages, and uploads fixtures to S3. + +## Build & Test +```bash +./gradlew build # compile + test + jar +./gradlew test # run all tests +./gradlew clean test # clean build + test +./gradlew jar # build fat JAR (all deps bundled) +./gradlew dependencies # list dependency tree +``` + +## Run Modes (CLI) +``` +-m MatchdaysUpdater - Update delivery dates from match times +-m MatchesCreatorFootball - Create match schedule from API-Football +-m MatchesUpdaterFootball - Update match team/datetime from API-Football +-m MatchesResultsUpdater - Push match results to website +-m TeamsUpdater - Add missing teams to DB +-m APIFootballUpdater - Fetch fixtures/rounds from API-Football → S3 +-m MatchesListGistUpdater - Update forum posts with fixture lists +-m PostChecksum - Print checksum of a config post +-m WhatsAppNotifier - Send WhatsApp reminders for missing bets +``` +Required: `-s -l -c ` + +## Architecture +``` +App.java (CLI entry) +├── apifootball/ — API-Football HTTP client, match model, S3 caching +├── googlecalendar/ — Google Calendar API CRUD for matchday events +├── teamidmatcher/ — BiMap: Tippliga team ID ↔ API-Football team ID +├── tippliga/ — Domain models & operations (match, matchday, team, league) +├── tippligaforum/ — MySQL DB connector, phpBB forum post management +└── whatsapp/ — OpenAI-generated reminders, WhatsApp API sender +``` + +## Code Conventions +- Package: `de.jeyp91.*` +- SQL columns referenced by ordinal position (constants like `final int SEASON = 1`) +- Logger via SLF4J: `LoggerFactory.getLogger(Class.class)` +- Config stored as JSON in phpBB forum posts (not local files) +- API keys / secrets in environment variables + +## Testing +- JUnit 4 + TestNG +- Tests in `src/test/java/de/jeyp91/` +- Run single test: `./gradlew test --tests "de.jeyp91.apifootball.APIFootballMatchTest"` diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b95aec --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# TLW Database Tool + +CLI tool for **Tippliga Würzburg** football prediction league administration. Syncs match data from [API-Football](https://www.api-football.com/), manages a MySQL database backing a phpBB forum, pushes Google Calendar events, sends WhatsApp reminders with AI-generated messages via OpenAI, and uploads fixture data to S3. + +## Architecture + +``` +App.java (CLI entry with argparse4j) +├── apifootball/ — API-Football HTTP client, match model, S3 caching +│ ├── APIFootballUpdater Fetch raw fixtures/rounds → S3 +│ ├── APIFootballConnector Singleton, reads cached S3 data, resolves matchdays +│ ├── APIFootballMatch API-Football match model (extends BaseMatch) +│ └── APIFootballMatchesProvider Resolves config entries to match lists +├── googlecalendar/ — Google Calendar API CRUD +│ ├── GoogleCalendarConnector OAuth2 + service setup +│ ├── CalendarConfigProvider Reads calendar ID/URL from JSON resource +│ └── TippligaGoogleEventManager Create/update/delete matchday events +├── teamidmatcher/ — Bi-directional ID mapping +│ ├── TeamIDMatcher HashBiMap: Tippliga team ID ↔ API-Football ID +│ └── TeamIDMatcherTemplateCreator Generates config template from API teams +├── tippliga/ — Domain models & operations +│ ├── TLWMatch / TLWMatchday / TLWTeam / TLWLeague (DB-mapped models) +│ ├── TLWMatchesCreatorFootball Build match schedule from API data +│ ├── TLWMatchesCreatorTipperLeague Build tipper-vs-tipper league matches +│ ├── TLWMatchesCreatorTipperPokal Build knockout tournament matches +│ ├── TLWMatchesUpdaterFootball Update teams/datetimes from API +│ ├── TLWMatchesResultsUpdater Push finished match results to website +│ ├── TLWMatchdaysCreator Compute delivery dates from match times +│ ├── TLWMatchdaysUpdater Write delivery date updates to DB + Calendar +│ ├── TLWTeamsCreator / TLWTeamsUpdater Team management +│ └── TLWMatchesManagerBase Shared helpers (date math, match matching) +├── tippligaforum/ — MySQL + phpBB integration +│ ├── TippligaSQLConnector Singleton JDBC connector (all queries) +│ ├── TippligaConfigProvider Reads JSON config from forum posts +│ ├── TippligaWebsiteConnector HTTP client for admin result updates +│ └── MatchesListCreator / MatchesListForumUpdater Fixture list posts +└── whatsapp/ — WhatsApp reminder system + ├── WhatsAppNotifier Sends messages via WhatsApp API + ├── OpenAIConnector Generates reminder text via GPT-3.5 + └── WhatsAppReminder Record: user, matchday, missing bets + +BaseMatch — Abstract match with status/comparison/shared fields +S3Provider — AWS S3 read/write for cached API-Football JSON +ResourceProvider — Reads JSON config files from classpath resources +StatusHolder — Global error flag +``` + +## Setup + +### Prerequisites +- Java 17+ +- MySQL database (phpBB with Tippliga extension) +- API-Football API key (free tier) +- Google Calendar API service account + credentials file +- AWS S3 bucket for fixture caching +- WhatsApp API endpoint + +### Environment Variables +| Variable | Description | +|---|---| +| `TLW_DATABASE_USERNAME` | MySQL username | +| `TLW_DATABASE_PASSWORD` | MySQL password | +| `FORUM_USERNAME` | phpBB admin username (for website connector) | +| `FORUM_PASSWORD` | phpBB admin password | +| `OPENAI_TOKEN` | OpenAI API token (GPT reminders) | +| `TLW_WHATSAPP_HOST` | WhatsApp API host | +| `TLW_WHATSAPP_PORT` | WhatsApp API port | +| `TLW_WHATSAPP_API_KEY` | WhatsApp API key | +| `AWS_ACCESS_KEY_ID` | AWS credentials (S3) | +| `AWS_SECRET_ACCESS_KEY` | AWS credentials (S3) | + +### Resources +Place these in `src/main/resources/`: +- `Tippliga/Team_ID_Matcher_Config.json` — team ID mapping +- `Tippliga/Ligen_v3.json` — league definitions +- `Google_Calendar_Config.json` — calendar IDs +- `Tippliga_Configs/.json` — per-season match configs + +Configs are stored as phpBB forum posts (JSON wrapped in `[code]` BBCode). + +## Usage + +```bash +# Build fat JAR +./gradlew jar + +# Run modes (all require -s -l -c ) +java -jar build/libs/tlw-database-tool-1.0.jar -m MatchdaysUpdater -s 2025 -l 1 -c tl_2025 +``` + +### CLI Modes + +| Mode | Function | +|---|---| +| `MatchdaysUpdater` | Update delivery dates from match times, sync Google Calendar | +| `MatchesCreatorFootball` | Create match schedule from API-Football | +| `MatchesUpdaterFootball` | Update match teams/datetimes from API-Football | +| `MatchesResultsUpdater` | Push finished results to website | +| `TeamsUpdater` | Add missing teams to DB | +| `APIFootballUpdater` | Fetch raw fixtures/rounds → S3 | +| `MatchesListGistUpdater` | Update forum posts with fixture lists | +| `PostChecksum` | Print MD5 checksum of a config post | +| `WhatsAppNotifier` | Send WhatsApp reminders for missing bets | + +### Build Commands +```bash +./gradlew build # compile + test + jar +./gradlew test # run all tests +./gradlew clean test # clean build + test +./gradlew jar # build fat JAR +./gradlew dependencies # list dependency tree +``` + +## Code Conventions +- Package: `de.jeyp91.*` +- SQL columns referenced by ordinal position in ResultSet (constants like `final int SEASON = 1`) +- Logger via SLF4J: `LoggerFactory.getLogger(Class.class)` +- Match status constants inherited from `BaseMatch` +- Config JSON stored in phpBB forum posts, loaded via `TippligaConfigProvider` +- Secrets in environment variables only + +## Testing +- JUnit 4 + TestNG +- Tests under `src/test/java/de/jeyp91/` +- Run single test: `./gradlew test --tests "de.jeyp91.apifootball.APIFootballMatchTest"` + +## How It Works (Data Flow) + +1. **APIFootballUpdater** fetches fixtures/rounds from API-Football → caches in S3 +2. **APIFootballConnector** reads cached S3 data, resolves API matchdays to Tippliga matchdays +3. **TLWMatchesCreatorFootball** config on which API matches map to which Tippliga matchdays → SQL INSERT +4. **TLWMatchdaysCreator** computes delivery deadlines from match times +5. **TLWMatchdaysUpdater** writes deadlines to DB + creates/updates Google Calendar events +6. **TLWMatchesUpdaterFootball** updates team IDs, datetimes from latest API data +7. **TLWMatchesResultsUpdater** pushes finished match results to the website via HTTP +8. **WhatsAppNotifier** queries DB for users with missing bets, generates GPT reminders, sends via WhatsApp API diff --git a/docs/apifootball.md b/docs/apifootball.md new file mode 100644 index 0000000..69d76b4 --- /dev/null +++ b/docs/apifootball.md @@ -0,0 +1,39 @@ +# apifootball — API-Football Integration + +Fetches, caches, and serves match data from [API-Football](https://www.api-football.com/). Raw JSON is cached in S3 to avoid repeated API calls. + +## Classes + +### APIFootballUpdater +HTTP client that fetches raw JSON from API-Football and writes to S3. + +- `updateAllFixtures(season)` — fetches all leagues' fixtures +- `updateAllRounds(season)` — fetches all leagues' round metadata +- `getRawData(requestPath, season, league)` — calls API-Football with rate-limit retry +- `getLeagues()` — reads league IDs from `Ligen_v3.json` resource + +Handles the Tippliga↔API-Football season offset: for most leagues, Tippliga season N corresponds to API-Football season N-1 (except World Cup `league=1` and Euro `league=4`). + +### APIFootballConnector +Singleton that reads cached S3 JSON and resolves API data into `APIFootballMatch` objects. + +- `getMatchDataByLeagueAndMatchday(league, matchday)` — all matches for a matchday +- `getMatchDataByLeagueAndMatchID(league, id)` — single match by API fixture ID +- `getMatchdays(leagueId)` — round metadata from S3 +- `getTeamsForLeague(season, league)` — live team list from API + +### APIFootballMatch +Extends `BaseMatch`. Parses a raw API-Football JSON fixture object. + +Parses: fixture ID, league ID, team IDs + names, round string, datetime, status, fulltime/extratime/penalty scores. + +Key method: `getMatchdayFromRoundString(season, round, leagueId)` — converts API round names (e.g. `"Regular Season - 1"`) to Tippliga matchday numbers. Falls back to the rounds JSON for non-regular-season formats. + +### APIFootballMatchesProvider +High-level resolver that reads matchday config (from the JSON config file) and returns flat `APIFootballMatch` lists. + +Supports two config entry types: +- `AllMatchesOfMatchday` — fetch all matches for a given API league+matchday +- `SingleMatch` — fetch a specific match by API league+fixture ID + +Honors `showTable` and `ko` flags from config. diff --git a/docs/googlecalendar.md b/docs/googlecalendar.md new file mode 100644 index 0000000..d4fcc0e --- /dev/null +++ b/docs/googlecalendar.md @@ -0,0 +1,29 @@ +# googlecalendar — Google Calendar Integration + +Manages matchday deadline events in Google Calendar. Each matchday can have up to 3 delivery deadlines (1st, 2nd, 3rd chance). + +## Classes + +### GoogleCalendarConnector +Sets up the Google Calendar API service using a service account. + +- Reads service account credentials from `tippliga-4e0def9ef447.json` (classpath resource) +- Uses `CalendarScopes.CALENDAR` scope +- Returns authenticated `com.google.api.services.calendar.Calendar` service + +### CalendarConfigProvider +Reads calendar ID and URL from `Google_Calendar_Config.json` resource. Currently hardcoded to league 1. + +### TippligaGoogleEventManager +CRUD operations for matchday deadline events. + +- `createOrUpdateEventsForMatchday(matchday)` — creates/updates up to 3 events per matchday +- `createNewEvent()` / `updateEvent()` / `deleteEvent()` — individual event operations +- `deleteAllEvents(season)` — wipe all events for a season +- `updateAllMatchdays(matchdays)` — batch update; automatically deletes events for removed delivery dates + +Event details: +- **Summary**: `"Tippliga Spieltag N tippen!"` (or custom matchday name) +- **Description**: season, league, matchday, delivery deadline number +- **Location**: `https://tippliga-wuerzburg.de/app.php/football/bet` +- **Time**: delivery deadline datetime, Europe/Berlin timezone diff --git a/docs/teamidmatcher.md b/docs/teamidmatcher.md new file mode 100644 index 0000000..1e87e2b --- /dev/null +++ b/docs/teamidmatcher.md @@ -0,0 +1,25 @@ +# teamidmatcher — Team ID Mapping + +Maintains a bi-directional mapping between Tippliga (phpBB) team IDs and API-Football team IDs. Uses a Guava `HashBiMap` loaded from `Team_ID_Matcher_Config.json`. + +## Classes + +### TeamIDMatcher +Singleton `HashBiMap` mapping Tippliga IDs ↔ API-Football IDs. + +- `getApiFootballIdFromTippligaId(id)` — Tippliga → API-Football +- `getTippligaIdFromApiFootballId(match, homeguest)` — API-Football → Tippliga, for a given match and team side +- `getTeamNameFromTippligaId(id)` — human-readable team name + +Sets `StatusHolder.setError()` if a lookup fails. Used by `TLWMatch`, `TLWMatchesUpdaterFootball`, `TLWMatchesResultsUpdater`. + +Config format (`Team_ID_Matcher_Config.json`): +```json +[ + { "tippligaID": 1, "teamname": "Team Name", "apiFootballID": 12345 }, + ... +] +``` + +### TeamIDMatcherTemplateCreator +Utility that fetches teams from API-Football for a given league and generates a JSON config template. Useful when setting up new seasons — outputs skeleton entries with `apiFootballID` pre-filled, `tippligaID` left blank. diff --git a/docs/tippliga.md b/docs/tippliga.md new file mode 100644 index 0000000..160e9bd --- /dev/null +++ b/docs/tippliga.md @@ -0,0 +1,99 @@ +# tippliga — Domain Models & Operations + +Core domain package with match, matchday, team, and league models plus operations for creating, updating, and managing the prediction league schedule. All extend `TLWMatchesManagerBase` or `TLWMatchesCreatorBase`. + +## Base Classes + +### BaseMatch (`de.jeyp91.BaseMatch`) +Abstract class with shared match state: team IDs, goals (home/guest/overtime/penalty), datetime, status, KO flag, show-table flag, and a `COMPARISON` enum (`IDENTICAL`, `DIFFERENT`, `DIFFERENT_DATETIME`, `DIFFERENT_RESULT`). + +Status constants: +- `STATUS_NOTSTARTED = 0` +- `STATUS_STARTED = 1` +- `STATUS_PROVISIONAL_RESULT_AVAILABLE = 2` +- `STATUS_FINISHED = 3` +- `STATUS_NOT_EVALUATED = 4` + +### TLWMatchesCreatorBase +Abstract creator with `TLWMatches` list, `getSQLInsertString()` generating `REPLACE INTO` queries, `getMatchesForMatchday()`, and `getNumberOfMatchdays()`. + +### TLWMatchesManagerBase +Shared helpers used by updaters: +- Date parsing from TLW (`yyyy-MM-dd HH:mm:ss`) and API-Football (`yyyy-MM-dd'T'HH:mm:ss`) formats +- `getFirstMatchtimeTLW()` / `getFirstMatchtimeAPIFootball()` — earliest match in a list +- `getDaysDifference()` — calendar day diff +- `getMatchesStartingFromSecondDay()` / `getMatchesStartingFromSecondWeek()` — filter helpers for delivery date computation +- `getMatchingMatch()` — matches an API match to a TLW match by home/guest team IDs + +## Domain Models + +### TLWMatch (extends BaseMatch) +Full match model mapped from `phpbb_footb_matches` table. Multiple constructors: +- From `ResultSet` (DB query) +- From `APIFootballMatch` + `TeamIDMatcher` (API sync) +- Manual constructors for creators +- Copy constructor + +Key methods: +- `getSQLQueryInsert()` / `getSQLQueryReplace()` — generates SQL values +- `updateMatch(APIFootballMatch)` — updates teams, goals, datetime from API data +- `isSameMatchPlaceholder()` — matches placeholder config entries to existing matches + +### TLWMatchday +Mapped from `phpbb_footb_matchdays`. Fields: season, league, matchday, status, 3 delivery dates, matchday name, match count. + +### TLWTeam +Mapped from `phpbb_footb_teams`. Fields: season, league, team ID, name, short name, symbol, group ID, matchday. + +### TLWLeague +Mapped from `phpbb_footb_leagues`. Fields: season, league ID, league name, short name. + +## Operations + +### TLWMatchesCreatorFootball +Builds match schedule from API-Football data and JSON config. For each matchday config entry: +1. Resolves API matches via `APIFootballMatchesProvider` +2. Creates `TLWMatch` objects with team IDs matched via `TeamIDMatcher` +3. Fills remaining slots with placeholder matches (empty teams) + +Handles `deliveryDateMode`: in `"single"` mode, matches beyond the first day get `status = -1` to prevent early betting on later matches. + +### TLWMatchesCreatorTipperLeague +Creates matches for tipper-vs-tipper leagues (where tippers are "teams"). Reads pairing config from `Tipper_Match_Pair_Config.json` and tipper-team mapping from `Tipper_Team_Config.json`. + +### TLWMatchesCreatorTipperPokal +Creates single-matchday knockout tournament matches from a tipper list config. Each pair of tippers is one match with `koMatch = 1` and `showTable = false`. + +### TLWMatchesUpdaterFootball +Compares current DB matches with latest API data and generates SQL UPDATE statements. Only operates on matchdays where the first match is still in the future. Updates: +- Team IDs (via `TeamIDMatcher`) +- Match datetimes +- Status flags (0 = current matchday, -1 = next-day match, -2 = later) +- `show_table` / `ko_match` flags + +Also handles: +- `addMissingMatches()` — inserts API matches not yet in DB (finds placeholder slots by datetime/formula) +- `addPlaceholderMatches()` — adds config-defined placeholders for TBD matchups + +### TLWMatchesResultsUpdater +For finished matches, compares DB goals with API goals and pushes updates to the website via `TippligaWebsiteConnector`. Detects changes to: +- Full-time goals +- Overtime goals (for KO matches with `betKOType = 2`) +- Penalty → overtime mapping (for `betKOType = 1` or `3`) +- Status transitions from provisional to confirmed + +### TLWMatchdaysCreator +Computes delivery dates (betting deadlines) from match times. Supports two modes: +- **"single"**: one deadline = first match time +- **"multiple"** (default): 3 deadlines — first match, first match of second day, first match of second week + +Also handles `additionalDeliveryDate` config flags for special matchday events. + +### TLWMatchdaysUpdater +Compares original DB matchdays with recomputed deadlines and generates SQL UPDATEs and Google Calendar syncs. Only updates future matchdays (deadline in the past is skipped). + +### TLWTeamsCreator +Extracts unique team IDs from a list of matches and generates `REPLACE INTO phpbb_footb_teams` SQL, preserving group and matchday info from match data. + +### TLWTeamsUpdater +Detects teams referenced in matches but not yet present in the league's team table. Generates INSERT SQL to add them (copies team metadata from existing entries in other seasons/leagues). diff --git a/docs/tippligaforum.md b/docs/tippligaforum.md new file mode 100644 index 0000000..c636ea6 --- /dev/null +++ b/docs/tippligaforum.md @@ -0,0 +1,48 @@ +# tippligaforum — MySQL & phpBB Integration + +Database connector, config loader, website HTTP client, and forum post management. + +## Classes + +### TippligaSQLConnector +Singleton JDBC connector to the phpBB MySQL database (`d0144ddb`). Connects to production via credentials from environment variables, with a fallback to localhost. + +All major queries in one class: +- `getTeams()`, `getMatches()`, `getMatchdays()`, `getLeague()` — read domain objects +- `getUpdatedMatchdaysBasedOnMatches()` — complex SQL that computes delivery deadlines from match times (uses `DATEDIFF` to split deadlines across days/weeks) +- `getNextWhatsAppReminders(hours)` — multi-table query finding users with missing bets on upcoming matchdays, joins with reminder tracking to avoid duplicates +- `updatePost()` / `getPost()` / `getChecksumOfPost()` — phpBB post CRUD +- `executeQuery()` / `executeUpdate()` — raw SQL access +- `markWhatsAppReminderAsSent()` — logs sent reminders + +Post updates use the phpBB checksum mechanism: `post_checksum` = MD5 of XML-escaped post text. + +### TippligaConfigProvider +Reads JSON configuration from phpBB forum posts. Configs are stored as topics under the `Admin → Tippliga-Config → ` forum hierarchy. + +- `getTippligaConfig(config)` — fetches post, strips `[code]` BBCode wrapper, parses JSON +- `getChecksumOfConfigPost(configFile)` — reads post checksum for integrity verification + +### TippligaWebsiteConnector +HTTP client that logs into the phpBB frontend + admin panel and submits match results. Used by `TLWMatchesResultsUpdater`. + +Login flow: +1. GET login page → extract `creation_time`, `form_token`, `sid` from hidden fields +2. POST credentials → capture session cookies +3. GET admin panel → extract admin credential token +4. POST admin login with `password_` parameter + +Result submission: +1. GET results management page → extract CSRF tokens +2. POST form with season, league, matchday, match scores (home, guest, overtime) + +### MatchesListCreator +Generates a JSON match list config from raw API-Football fixture data (read from S3). Creates `SingleMatch` config entries with matchday, matchtime, team names, and fixture IDs. Used by `MatchesListForumUpdater` to create fixture list forum posts. + +### MatchesListForumUpdater +For each league: +1. Creates `MatchesListCreator` from S3 fixture data +2. Formats as pretty-printed JSON +3. Wraps in `[code]` BBCode +4. Finds the correct forum post (under `Admin → Tippliga-Config → → Ligen → `) +5. Updates the post text via `TippligaSQLConnector` diff --git a/docs/whatsapp.md b/docs/whatsapp.md new file mode 100644 index 0000000..248045e --- /dev/null +++ b/docs/whatsapp.md @@ -0,0 +1,40 @@ +# whatsapp — WhatsApp Reminder System + +Sends personalized WhatsApp reminders to users who haven't submitted their bets before the deadline. Uses OpenAI GPT-3.5 to generate natural-language reminder messages. + +## Classes + +### WhatsAppNotifier +Orchestrates the reminder workflow: + +1. Queries DB for upcoming reminders at 2 checkpoints: **24 hours** and **2 hours** before deadline +2. For each reminder, generates a message via `OpenAIConnector` +3. Sends via WhatsApp API (HTTP POST to configurable host:port) +4. Marks sent reminders in DB to avoid duplicates + +Requires env vars: `TLW_WHATSAPP_HOST`, `TLW_WHATSAPP_PORT`, `TLW_WHATSAPP_API_KEY`. + +### OpenAIConnector +Caches and generates reminder text via OpenAI Chat Completions API (model `gpt-3.5-turbo-0125`). + +Prompt structure: +- **System prompt** (German): "Generate a message reminding someone to submit missing tips. Must sound like a friend. Use correct singular/plural." +- **User input**: JSON with league name, matchday name, delivery day, time +- **Output template** has 2 variants — 24h deadline uses a friendly intro, 2h deadline emphasizes urgency + +Message format: +``` +Hey , + +Bitte gib die fehlenden Tipps bis um ab. +Du kannst deine Tipps wie immer unter https://tippliga-wuerzburg.de/app.php/football/bet abgeben. +Viele Grüße +Die Tippliga Admins +``` + +Messages are cached per (league + matchday + delivery) key to avoid repeated API calls. + +### WhatsAppReminder +Java `record` with fields: season, league, leagueName, matchday, matchdayName, deliveryDate, todayTomorrow, time, remainingHours, userId, username, phoneNumber, missingBets. + +Maps directly to the SQL query result from `TippligaSQLConnector.getNextWhatsAppReminders()`. diff --git a/jenkinsfiles/LigaCupUpdater/Jenkinsfile b/jenkinsfiles/LigaCupUpdater/Jenkinsfile index ecf4c6a..b8e87be 100644 --- a/jenkinsfiles/LigaCupUpdater/Jenkinsfile +++ b/jenkinsfiles/LigaCupUpdater/Jenkinsfile @@ -14,7 +14,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'), usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), - usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD'), + string(credentialsId: 'google_calendar_private_key_id', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID'), + string(credentialsId: 'google_calendar_private_key', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY') ]) { withEnv([ "JAVA_HOME=${jdkPath}/jdk-22.0.2" diff --git a/jenkinsfiles/RelegationUpdater/Jenkinsfile b/jenkinsfiles/RelegationUpdater/Jenkinsfile index 3b6ebb8..a803d9c 100644 --- a/jenkinsfiles/RelegationUpdater/Jenkinsfile +++ b/jenkinsfiles/RelegationUpdater/Jenkinsfile @@ -14,7 +14,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'), usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), - usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD'), + string(credentialsId: 'google_calendar_private_key_id', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID'), + string(credentialsId: 'google_calendar_private_key', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY') ]) { withEnv([ "JAVA_HOME=${jdkPath}/jdk-22.0.2" diff --git a/jenkinsfiles/SupercupUpdater/Jenkinsfile b/jenkinsfiles/SupercupUpdater/Jenkinsfile index 3040950..3c352bd 100644 --- a/jenkinsfiles/SupercupUpdater/Jenkinsfile +++ b/jenkinsfiles/SupercupUpdater/Jenkinsfile @@ -14,7 +14,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'), usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), - usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD'), + string(credentialsId: 'google_calendar_private_key_id', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID'), + string(credentialsId: 'google_calendar_private_key', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY') ]) { withEnv([ "JAVA_HOME=${jdkPath}/jdk-22.0.2" diff --git a/jenkinsfiles/TippligaUpdater/Jenkinsfile b/jenkinsfiles/TippligaUpdater/Jenkinsfile index e02f7e1..614d35e 100644 --- a/jenkinsfiles/TippligaUpdater/Jenkinsfile +++ b/jenkinsfiles/TippligaUpdater/Jenkinsfile @@ -14,7 +14,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'), usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), - usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD'), + string(credentialsId: 'google_calendar_private_key_id', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID'), + string(credentialsId: 'google_calendar_private_key', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY') ]) { withEnv([ "JAVA_HOME=${jdkPath}/jdk-22.0.2" diff --git a/jenkinsfiles/WTLPokalUpdater/Jenkinsfile b/jenkinsfiles/WTLPokalUpdater/Jenkinsfile index 72f222a..2eb7843 100644 --- a/jenkinsfiles/WTLPokalUpdater/Jenkinsfile +++ b/jenkinsfiles/WTLPokalUpdater/Jenkinsfile @@ -14,7 +14,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'aws', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'), usernamePassword(credentialsId: 'forum_database', usernameVariable: 'TLW_DATABASE_USERNAME', passwordVariable: 'TLW_DATABASE_PASSWORD'), - usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD') + usernamePassword(credentialsId: 'forum_user', usernameVariable: 'FORUM_USERNAME', passwordVariable: 'FORUM_PASSWORD'), + string(credentialsId: 'google_calendar_private_key_id', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID'), + string(credentialsId: 'google_calendar_private_key', variable: 'GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY') ]) { withEnv([ "JAVA_HOME=${jdkPath}/jdk-22.0.2" diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..f0c3d4c --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +java = "22.0.2" diff --git a/src/main/java/de/jeyp91/googlecalendar/CalendarConfigProvider.java b/src/main/java/de/jeyp91/googlecalendar/CalendarConfigProvider.java index f3bad9c..a8a2ca1 100644 --- a/src/main/java/de/jeyp91/googlecalendar/CalendarConfigProvider.java +++ b/src/main/java/de/jeyp91/googlecalendar/CalendarConfigProvider.java @@ -1,45 +1,14 @@ package de.jeyp91.googlecalendar; -import com.google.common.io.Resources; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; - -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; - public class CalendarConfigProvider { - private static JSONObject googleCalendarConfig = null; - - private static JSONObject getGoogleCalendarConfig() { - if(googleCalendarConfig == null) { - //JSON parser object to parse read file - JSONParser jsonParser = new JSONParser(); - URL url = Resources.getResource("Google_Calendar_Config.json"); - String jsonConfig = null; - - try { - jsonConfig = Resources.toString(url, StandardCharsets.UTF_8); - //Read JSON file - googleCalendarConfig = (JSONObject) jsonParser.parse(jsonConfig); - - } catch (IOException | ParseException e) { - e.printStackTrace(); - } - - } - return googleCalendarConfig; - } + private static String GOOGLE_CALENDAR_ID = "825f79shtm9n3uknj99iuu2qho"; public static String getCalendarUrl() { - JSONObject leagueConfig = (JSONObject) getGoogleCalendarConfig().get(String.valueOf(1)); - return (String) leagueConfig.get("url"); + return GOOGLE_CALENDAR_ID + "@group.calendar.google.com"; } public static String getCalendarId() { - JSONObject leagueConfig = (JSONObject) getGoogleCalendarConfig().get(String.valueOf(1)); - return (String) leagueConfig.get("id"); + return GOOGLE_CALENDAR_ID; } } diff --git a/src/main/java/de/jeyp91/googlecalendar/GoogleCalendarConnector.java b/src/main/java/de/jeyp91/googlecalendar/GoogleCalendarConnector.java index c32fd4a..211d24a 100644 --- a/src/main/java/de/jeyp91/googlecalendar/GoogleCalendarConnector.java +++ b/src/main/java/de/jeyp91/googlecalendar/GoogleCalendarConnector.java @@ -8,33 +8,69 @@ import com.google.api.services.calendar.Calendar; import com.google.api.services.calendar.CalendarScopes; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; -import java.io.*; +import java.io.IOException; +import java.net.URI; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; import java.util.Collections; public class GoogleCalendarConnector { private static final String APPLICATION_NAME = "Google Calendar API Java Quickstart"; private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); - /** - * Global instance of the scopes required by this quickstart. - * If modifying these scopes, delete your previously saved tokens/ folder. - */ - private static final String SERVICE_CREDENTIALS_FILE_PATH = "/tippliga-4e0def9ef447.json"; + private static final String PROJECT_ID = "tippliga"; + private static final String CLIENT_MAIL = "tlw-707@tippliga.iam.gserviceaccount.com"; + private static final String CLIENT_ID = "101590566618120833710"; + private static final String TOKEN_URI = "https://oauth2.googleapis.com/token"; - /** - * Creates an authorized Credential object. - * @return An authorized Credential object. - * @throws IOException If the Google_Credentials.json file cannot be found. - */ private static GoogleCredentials getGoogleCredentials() throws IOException { - InputStream in = GoogleCalendarConnector.class.getResourceAsStream(SERVICE_CREDENTIALS_FILE_PATH); - GoogleCredentials credentials = GoogleCredentials.fromStream(in) + String privateKeyId = getEnv("GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY_ID"); + String privateKeyPem = getEnv("GOOGLE_CALENDAR_TIPPLIGA_SERVICE_ACCOUNT_PRIVATE_KEY"); + + GoogleCredentials credentials = ServiceAccountCredentials.newBuilder() + .setClientEmail(CLIENT_MAIL) + .setClientId(CLIENT_ID) + .setPrivateKey(parsePrivateKey(privateKeyPem)) + .setPrivateKeyId(privateKeyId) + .setProjectId(PROJECT_ID) + .setTokenServerUri(URI.create(TOKEN_URI)) + .build() .createScoped(Collections.singleton(CalendarScopes.CALENDAR)); + credentials.refreshIfExpired(); return credentials; } + private static PrivateKey parsePrivateKey(String pemData) throws IOException { + String normalized = pemData.replace("\\n", "\n"); + String stripped = normalized + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s", ""); + byte[] encoded = Base64.getDecoder().decode(stripped); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new IOException("Failed to parse private key", e); + } + } + + private static String getEnv(String name) throws IOException { + String value = System.getenv(name); + if (value == null || value.isEmpty()) { + throw new IOException("Environment variable " + name + " is not set"); + } + return value; + } + public static com.google.api.services.calendar.Calendar getSerivce() { Calendar service = null; diff --git a/src/main/java/de/jeyp91/openligadb/OpenLigaDBConnector.java b/src/main/java/de/jeyp91/openligadb/OpenLigaDBConnector.java deleted file mode 100644 index 5b9fe10..0000000 --- a/src/main/java/de/jeyp91/openligadb/OpenLigaDBConnector.java +++ /dev/null @@ -1,142 +0,0 @@ -package de.jeyp91.openligadb; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; - -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClientBuilder; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; - -/** - * - */ -public class OpenLigaDBConnector { - - private final String OPENLIGADB_API_URL = "http://www.openligadb.de/api/"; - - public OpenLigaDBConnector() {} - - public OpenLigaDBMatch getMatchDataOfSingleMatch(int id) { - String url = OPENLIGADB_API_URL + "getmatchdata/" + id; - JSONObject matchAsJson = getDataAsJSONObject(url); - return new OpenLigaDBMatch(matchAsJson); - } - - public ArrayList getMatchDataOfMatchday(int season, String league, int matchday) { - - String url = OPENLIGADB_API_URL + "getmatchdata/" + league + "/" + (season - 1) + "/" + matchday; - - JSONArray matches = getDataAsJSONArray(url); - ArrayList matchesList = new ArrayList<>(); - - for(Object match: matches) { - matchesList.add(new OpenLigaDBMatch((JSONObject) match)); - } - - return matchesList; - } - - public ArrayList getMatchDataOfCurrentMatchday(String league) { - - String url = OPENLIGADB_API_URL + "getmatchdata/" + league; - - JSONArray matches = getDataAsJSONArray(url); - ArrayList matchesList = new ArrayList<>(); - - for(Object match: matches) { - matchesList.add(new OpenLigaDBMatch((JSONObject) match)); - } - - return matchesList; - } - - public OpenLigaDBMatch getMatchDataByMatchId(int id) { - String url = OPENLIGADB_API_URL + "getmatchdata/" + id; - JSONObject match = getDataAsJSONObject(url); - return new OpenLigaDBMatch(match); - } - - private JSONArray getDataAsJSONArray(String requestUrl) { - - String rawData = getRawData(requestUrl); - JSONParser parser = new JSONParser(); - JSONArray data = null; - try { - data = (JSONArray) parser.parse(rawData); - } catch (ParseException e) { - /* TODO */ - e.printStackTrace(); - } - return data; - } - - private JSONObject getDataAsJSONObject(String requestUrl) { - - String rawData = getRawData(requestUrl); - JSONParser parser = new JSONParser(); - JSONObject data = null; - try { - data = (JSONObject) parser.parse(rawData); - } catch (ParseException e) { - /* TODO */ - e.printStackTrace(); - } - return data; - } - - private String getRawData(String requestUrl) { - - HttpClient client = HttpClientBuilder.create().build(); - HttpGet request = new HttpGet(requestUrl); - - // add request header - request.addHeader("Content-Type", "application/json"); - - HttpResponse response = null; - try { - response = client.execute(request); - } catch (ClientProtocolException e) { - /* TODO */ - e.printStackTrace(); - } catch (IOException e) { - /* TODO */ - e.printStackTrace(); - } - - - BufferedReader rd = null; - try { - rd = new BufferedReader( - new InputStreamReader(response.getEntity().getContent()) - ); - } catch (IOException e) { - /* TODO */ - e.printStackTrace(); - } - - StringBuilder result = new StringBuilder(); - String line = ""; - while (true) { - try { - line = rd.readLine(); - } catch (IOException e) { - /* TODO */ - e.printStackTrace(); - } - - // Stop reading if last line was found. - if (line == null) break; - - result.append(line); - } - return result.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/de/jeyp91/openligadb/OpenLigaDBMatch.java b/src/main/java/de/jeyp91/openligadb/OpenLigaDBMatch.java deleted file mode 100644 index 91baac2..0000000 --- a/src/main/java/de/jeyp91/openligadb/OpenLigaDBMatch.java +++ /dev/null @@ -1,88 +0,0 @@ -package de.jeyp91.openligadb; - -import de.jeyp91.BaseMatch; -import de.jeyp91.tippliga.TLWMatch; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import java.util.regex.*; - -/** - * - */ -public class OpenLigaDBMatch extends BaseMatch { - - private final int RESULT_TYPE_ENDRESULT = 2; - - private Integer season = null; - private int matchId; - private int leagueId; - private boolean matchIsFinished; - - public OpenLigaDBMatch(JSONObject json) { - this.matchId = Integer.parseInt(json.get("MatchID").toString()); - this.season = getSeasonFromLeageName(json.get("LeagueName").toString()); - this.leagueId = Integer.parseInt(json.get("LeagueId").toString()); - this.teamIdHome = Integer.parseInt(((JSONObject) json.get("Team1")).get("TeamId").toString()); - this.teamIdGuest = Integer.parseInt(((JSONObject) json.get("Team2")).get("TeamId").toString()); - for(int i = 0; i < ((JSONArray) json.get("MatchResults")).size(); i++) { - if(Integer.parseInt((((JSONObject) ((JSONArray) json.get("MatchResults")).get(i)).get("ResultTypeID")).toString()) == RESULT_TYPE_ENDRESULT) { - this.goalsHome = Integer.parseInt(((JSONObject) ((JSONArray) json.get("MatchResults")).get(i)).get("PointsTeam1").toString()); - this.goalsGuest = Integer.parseInt(((JSONObject) ((JSONArray) json.get("MatchResults")).get(i)).get("PointsTeam2").toString()); - } - } - this.matchday = Integer.parseInt(((JSONObject) json.get("Group")).get("GroupOrderID").toString()); - this.matchIsFinished = (Boolean) json.get("MatchIsFinished"); - this.matchDatetime = (String) json.get("MatchDateTime"); - } - - private int getSeasonFromLeageName(String leagueName) { - Pattern p = Pattern.compile("\\d*/\\d*"); - Matcher m = p.matcher(leagueName); - m.find(); - String seasonString = m.group(); - p = Pattern.compile("\\d+"); - m = p.matcher(seasonString); - Integer season = null; - while(m.find()) { - season = Integer.parseInt(m.group()); - } - return season < 100 ? season + 2000 : season; - } - - public boolean isSameMatch(TLWMatch compareMatch) { - if(this.getSeason()!= compareMatch.getSeason()) { - return false; - } - if(this.getMatchday() != compareMatch.getMatchday()) { - return false; - } - if(this.getTeamIdHome() != compareMatch.getTeamIdHome()) { - return false; - } - if(this.getTeamIdGuest() != compareMatch.getTeamIdGuest()) { - return false; - } - String thisDateTime = this.getMatchDateTime().replace("T", " "); - String tempDateTime = compareMatch.getMatchDateTime().replace("T", " "); - if(!tempDateTime.equals(thisDateTime)) { - return false; - } - if(this.goalsHome != compareMatch.getGoalsHome() || - this.goalsGuest != compareMatch.getGoalsGuest()) { - return false; - } - return true; - } - - public Integer getSeason() { - return this.season; - } - - public Integer getMatchId() { - return this.matchId; - } - - public boolean getMatchIsFinished() { - return this.matchIsFinished; - } -} \ No newline at end of file diff --git a/src/main/resources/Gist_Config.json b/src/main/resources/Gist_Config.json deleted file mode 100644 index 9f4d963..0000000 --- a/src/main/resources/Gist_Config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "username": "tippligawuerzburg", - "password": "1154f448eae29212971cb82ca8850f54bff23629" -} \ No newline at end of file diff --git a/src/main/resources/Google_Calendar_Config.json b/src/main/resources/Google_Calendar_Config.json deleted file mode 100644 index 56202a4..0000000 --- a/src/main/resources/Google_Calendar_Config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "1": { - "id": "825f79shtm9n3uknj99iuu2qho", - "url": "825f79shtm9n3uknj99iuu2qho@group.calendar.google.com" - } -} \ No newline at end of file diff --git a/src/main/resources/Google_Credentials.json b/src/main/resources/Google_Credentials.json deleted file mode 100644 index 716dc42..0000000 --- a/src/main/resources/Google_Credentials.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "installed": { - "client_id": "267204101826-2ps9m6httllrc7mrsvml50rsfb661k87.apps.googleusercontent.com", - "project_id": "tippliga", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_secret": "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", - "redirect_uris": [ - "urn:ietf:wg:oauth:2.0:oob", - "http://localhost" - ] - } -} \ No newline at end of file diff --git a/src/main/resources/Tippliga_Configs/wtl_2023.json b/src/main/resources/Tippliga_Configs/2023/wtl_2023.json similarity index 100% rename from src/main/resources/Tippliga_Configs/wtl_2023.json rename to src/main/resources/Tippliga_Configs/2023/wtl_2023.json diff --git a/src/main/resources/Tippliga_Configs/wtl_2024.json b/src/main/resources/Tippliga_Configs/2024/wtl_2024.json similarity index 100% rename from src/main/resources/Tippliga_Configs/wtl_2024.json rename to src/main/resources/Tippliga_Configs/2024/wtl_2024.json diff --git a/src/main/resources/Tippliga_Configs/tl_2025.json b/src/main/resources/Tippliga_Configs/2025/tl_2025.json similarity index 100% rename from src/main/resources/Tippliga_Configs/tl_2025.json rename to src/main/resources/Tippliga_Configs/2025/tl_2025.json diff --git a/src/main/resources/Tippliga_Configs/tl_2025_v3.json b/src/main/resources/Tippliga_Configs/2025/tl_2025_v3.json similarity index 100% rename from src/main/resources/Tippliga_Configs/tl_2025_v3.json rename to src/main/resources/Tippliga_Configs/2025/tl_2025_v3.json diff --git a/src/main/resources/Tippliga_Configs/wtl_2025.json b/src/main/resources/Tippliga_Configs/2025/wtl_2025.json similarity index 100% rename from src/main/resources/Tippliga_Configs/wtl_2025.json rename to src/main/resources/Tippliga_Configs/2025/wtl_2025.json diff --git a/src/main/resources/Tippliga_Configs/wtl_2025_v3.json b/src/main/resources/Tippliga_Configs/2025/wtl_2025_v3.json similarity index 100% rename from src/main/resources/Tippliga_Configs/wtl_2025_v3.json rename to src/main/resources/Tippliga_Configs/2025/wtl_2025_v3.json diff --git a/src/main/resources/Tippliga_Configs/lc_2026.json b/src/main/resources/Tippliga_Configs/2026/lc.json similarity index 100% rename from src/main/resources/Tippliga_Configs/lc_2026.json rename to src/main/resources/Tippliga_Configs/2026/lc.json diff --git a/src/main/resources/Tippliga_Configs/tl_2026_v3.json b/src/main/resources/Tippliga_Configs/2026/tl.json similarity index 100% rename from src/main/resources/Tippliga_Configs/tl_2026_v3.json rename to src/main/resources/Tippliga_Configs/2026/tl.json diff --git a/src/main/resources/Tippliga_Configs/tl_2026_ligen.sql b/src/main/resources/Tippliga_Configs/2026/tl_ligen.sql similarity index 100% rename from src/main/resources/Tippliga_Configs/tl_2026_ligen.sql rename to src/main/resources/Tippliga_Configs/2026/tl_ligen.sql diff --git a/src/main/resources/Tippliga_Configs/wtl_2026.json b/src/main/resources/Tippliga_Configs/2026/wtl.json similarity index 100% rename from src/main/resources/Tippliga_Configs/wtl_2026.json rename to src/main/resources/Tippliga_Configs/2026/wtl.json diff --git a/src/main/resources/tippliga_config_template.json b/src/main/resources/tippliga_config_template.json deleted file mode 100644 index 675673d..0000000 --- a/src/main/resources/tippliga_config_template.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "numberOfMatchdays": 39, - "matchesPerMatchday": 12, - "pointMode": 4, - "pointsTendency": 1, - "pointsDifference": 2, - "pointsDirectHit": 3, - "ko": 0, - "matchdayConfig": [ - { - "TLWMatchday": 1, - "matchesConfig": [] - }, - { - "TLWMatchday": 2, - "matchesConfig": [] - }, - { - "TLWMatchday": 3, - "matchesConfig": [] - }, - { - "TLWMatchday": 4, - "matchesConfig": [] - }, - { - "TLWMatchday": 5, - "matchesConfig": [] - }, - { - "TLWMatchday": 6, - "matchesConfig": [] - }, - { - "TLWMatchday": 7, - "matchesConfig": [] - }, - { - "TLWMatchday": 8, - "matchesConfig": [] - }, - { - "TLWMatchday": 9, - "matchesConfig": [] - }, - { - "TLWMatchday": 10, - "matchesConfig": [] - }, - { - "TLWMatchday": 11, - "matchesConfig": [] - }, - { - "TLWMatchday": 12, - "matchesConfig": [] - }, - { - "TLWMatchday": 13, - "matchesConfig": [] - }, - { - "TLWMatchday": 14, - "matchesConfig": [] - }, - { - "TLWMatchday": 15, - "matchesConfig": [] - }, - { - "TLWMatchday": 16, - "matchesConfig": [] - }, - { - "TLWMatchday": 17, - "matchesConfig": [] - }, - { - "TLWMatchday": 18, - "matchesConfig": [] - }, - { - "TLWMatchday": 19, - "matchesConfig": [] - }, - { - "TLWMatchday": 20, - "matchesConfig": [] - }, - { - "TLWMatchday": 21, - "matchesConfig": [] - }, - { - "TLWMatchday": 22, - "matchesConfig": [] - }, - { - "TLWMatchday": 23, - "matchesConfig": [] - }, - { - "TLWMatchday": 24, - "matchesConfig": [] - }, - { - "TLWMatchday": 25, - "matchesConfig": [] - }, - { - "TLWMatchday": 26, - "matchesConfig": [] - }, - { - "TLWMatchday": 27, - "matchesConfig": [] - }, - { - "TLWMatchday": 28, - "matchesConfig": [] - }, - { - "TLWMatchday": 29, - "matchesConfig": [] - }, - { - "TLWMatchday": 30, - "matchesConfig": [] - }, - { - "TLWMatchday": 31, - "matchesConfig": [] - }, - { - "TLWMatchday": 32, - "matchesConfig": [] - }, - { - "TLWMatchday": 33, - "matchesConfig": [] - }, - { - "TLWMatchday": 34, - "matchesConfig": [] - }, - { - "TLWMatchday": 35, - "matchesConfig": [] - }, - { - "TLWMatchday": 36, - "matchesConfig": [] - }, - { - "TLWMatchday": 37, - "matchesConfig": [] - }, - { - "TLWMatchday": 38, - "matchesConfig": [] - }, - { - "TLWMatchday": 39, - "matchesConfig": [] - } - ] -} \ No newline at end of file diff --git a/src/test/java/de/jeyp91/TeamIDMatcherTest.java b/src/test/java/de/jeyp91/TeamIDMatcherTest.java index 8db27bc..2aff719 100644 --- a/src/test/java/de/jeyp91/TeamIDMatcherTest.java +++ b/src/test/java/de/jeyp91/TeamIDMatcherTest.java @@ -3,7 +3,6 @@ import de.jeyp91.apifootball.APIFootballMatch; import de.jeyp91.teamidmatcher.TeamIDMatcher; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -11,7 +10,7 @@ import static org.junit.Assert.assertEquals; public class TeamIDMatcherTest { @Test - public void getOpenLigaDbIdFromTippligaIdTest() { + public void getAPIFootballIdFromTippligaIdTest() { int apiFootballId; apiFootballId = TeamIDMatcher.getApiFootballIdFromTippligaId(505); @@ -23,7 +22,7 @@ public class TeamIDMatcherTest { } @Test - public void getTippligaIdFromOpenLigaDbIdTest() throws Exception { + public void getTippligaIdFromAPIFootballIdTest() throws Exception { int tlwId; String jsonMatch = "{" + "\"fixture_id\":1," + diff --git a/src/test/java/de/jeyp91/apifootball/APIFootballConnectorTest.java b/src/test/java/de/jeyp91/apifootball/APIFootballConnectorTest.java index 03227a2..f9448a2 100644 --- a/src/test/java/de/jeyp91/apifootball/APIFootballConnectorTest.java +++ b/src/test/java/de/jeyp91/apifootball/APIFootballConnectorTest.java @@ -1,6 +1,5 @@ package de.jeyp91.apifootball; -import de.jeyp91.openligadb.OpenLigaDBConnector; import org.junit.Test; import java.util.ArrayList;