A Discord bot that monitors Swedish basketball league rosters (SBL Herr / SBL Dam) and notifies you when key players are missing from game rosters.
- Live Roster Monitoring: Fetches game rosters from FIBA LiveStats/GeniusSports
- Season Stats Integration: Pulls player statistics from Eurobasket.com
- Key Player Detection: Identifies key players based on MPG threshold (default: 15.0 MPG)
- Smart Name Matching: Fuzzy matching between different data sources with confidence scoring
- Out Streak Tracking: Tracks consecutive missed games for players
- Discord Notifications: Rich embeds with player stats and streak information
- Deduplication: Avoids sending duplicate alerts for the same game/team
- Node.js 20+
- npm or yarn
- A Discord webhook URL
-
Clone and install dependencies:
cd DiscordBot npm install -
Configure environment:
cp .env.example .env
Edit
.envand set your Discord webhook URL:DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN -
Initialize the database:
npm run db:init
-
Sync player data from Eurobasket:
npm run sync-eurobasket
-
Test Discord integration:
npm run test-discord
npm run watchThis will:
- Poll for upcoming games every 5 minutes (configurable)
- Fetch rosters when they become available (typically 15-30 min before tip-off)
- Compare against Eurobasket season stats
- Send Discord alerts for missing key players
npm run check-game <gameId>
# Example: npm run check-game 123456# List teams in database
npm start -- list-teams
# List unmatched players (for manual mapping)
npm start -- list-unmatched
# Show current configuration
npm start -- show-config
# Sync Eurobasket data for specific league
npm start -- sync-eurobasket --league SBL_HERRAll configuration is done via environment variables (see .env.example):
| Variable | Default | Description |
|---|---|---|
DISCORD_WEBHOOK_URL |
(required) | Discord webhook URL for notifications |
DATABASE_PATH |
./data/roster.db |
SQLite database file path |
LOG_LEVEL |
info |
Logging level: debug, info, warn, error |
KEY_PLAYER_MIN_MPG |
15.0 |
Minimum MPG to be considered a key player |
FUZZY_MATCH_THRESHOLD |
75 |
Minimum confidence % for name matching |
ROSTER_POLL_INTERVAL_MINUTES |
5 |
How often to poll for new rosters |
PRE_GAME_POLL_START_MINUTES |
90 |
When to start polling before tip-off |
DAYS_AHEAD |
1 |
Days ahead to look for games |
EUROBASKET_CACHE_MINUTES |
60 |
Cache duration for Eurobasket data |
TEST_MODE |
false |
If true, logs Discord messages instead of sending |
๐ SBL Herr: Team A vs Team B
HOME team missing key players
โ Player Name
๐ 25.5 MPG / 18.2 PPG / 5.1 RPG / 3.4 APG
โฎ๏ธ Played previous: No
๐ข 3 consecutive games missed
๐
Game Info
Date: 2026-02-06
Time: 18:00
The bot needs to discover game schedules. The URLs are configured in src/config.ts:
leagues: {
SBL_HERR: {
fibaScheduleUrl: 'https://fibalivestats.dcd.shared.geniussports.com/data/competition/schedule.json',
eurobasketUrl: 'https://basketball.eurobasket.com/Sweden/basketball-league-Swedish-Basketligan',
},
SBL_DAM: {
fibaScheduleUrl: 'https://fibalivestats.dcd.shared.geniussports.com/data/competition/schedule.json',
eurobasketUrl: 'https://basketball.eurobasket.com/Sweden/basketball-league-Swedish-Basketligan-Women',
},
}-
Find the correct FIBA LiveStats URL:
- Go to the Swedish Basketball Federation site or FIBA website
- Find a live game and inspect the network requests
- Look for URLs containing
fibalivestatsorlivestats.dcd.shared.geniussports.com - The schedule endpoint typically follows patterns like:
https://fibalivestats.dcd.shared.geniussports.com/data/{competitionId}/schedule.jsonhttps://livestats.dcd.shared.geniussports.com/u/SBBF/
-
Update
src/config.ts:fibaScheduleUrl: 'YOUR_NEW_URL_HERE',
-
Add alternative sources in
alternativeScheduleSources:alternativeScheduleSources: { SBL_HERR: [ 'https://www.basket.se/tavling/serier-och-matcher/?serie=sbl-herr', // Add more URLs here ], }
If you can't find an automatic schedule URL, you can manually check games:
- Find the game page URL (e.g.,
https://fibalivestats.dcd.shared.geniussports.com/u/SBBF/2345678/) - Extract the game ID (
2345678) - Run:
npm run check-game 2345678
The SQLite database stores:
- teams: Team information and mappings
- players: Player stats from Eurobasket
- name_mappings: Fuzzy match mappings between FIBA and Eurobasket names
- games: Game schedule and status
- player_game_participation: Who played in which games
- alerts_sent: Deduplication tracking
- cache: Temporary cache for API responses
- unmatched_players: Players that couldn't be matched (for review)
When the fuzzy matcher can't confidently match a FIBA name to an Eurobasket player:
-
Run
npm start -- list-unmatchedto see unmatched names -
The output shows the best match and confidence score
-
If names are clearly the same person, you can add a manual mapping:
-- In SQLite INSERT INTO manual_name_overrides (fiba_name, eurobasket_name, team_id) VALUES ('J. Smith', 'John Smith', 1);
src/
โโโ index.ts # CLI entry point
โโโ config.ts # Configuration management
โโโ sources/
โ โโโ fiba.ts # FIBA LiveStats parser
โ โโโ eurobasket.ts # Eurobasket.com parser
โโโ matching/
โ โโโ normalize.ts # Name normalization (diacritics, etc.)
โ โโโ match.ts # Fuzzy matching logic
โโโ db/
โ โโโ schema.sql # Database schema
โ โโโ db.ts # Database connection
โ โโโ repo.ts # Data access layer
โโโ logic/
โ โโโ compare.ts # Roster comparison
โ โโโ streaks.ts # Missed game streak tracking
โโโ notify/
โ โโโ discord.ts # Discord webhook integration
โโโ utils/
โโโ logger.ts # Logging configuration
- Rosters are typically published 15-30 minutes before tip-off
- Try again closer to game time
- Run
npm run sync-eurobasketto populate team data
- Check
npm start -- list-unmatched - Add manual mapping if needed
- Lower
FUZZY_MATCH_THRESHOLDif too strict
- Verify webhook URL in
.env - Run
npm run test-discordto test - Check
logs/error.logfor details
# Run with debug logging
LOG_LEVEL=debug npm run watch
# Test mode (logs instead of sending)
TEST_MODE=true npm run check-game 123456
# Type checking
npm run typecheckMIT