Modular Discord bot for MyFly Club with three feature areas:
- Oil monitoring: polls oil prices, posts updates, and renames the oil channel.
- Route of the Day (ROTD): generates and posts route reports manually or on schedule.
- Aviation info: live lookups for airplane models, airports, and route research data.
The bot is slash-command only (/) and runs with a module-based runtime (oil, rotd, aviation_info, ops) plus crash recovery, retries, and HTTP circuit breaker support.
- Slash command interface only (no prefix commands).
- Modular architecture:
oilmodule: monitoring, health, stats, channel rename.rotdmodule: random route command + optional daily scheduler.aviation_infomodule:/plane,/airport,/research.opsmodule: crash and system diagnostics.
- Supervised mode (
RUN_SUPERVISED) for crash recovery and auto-restart. - Retry helpers for Discord API operations.
- HTTP circuit breaker for external API calls.
- Aggregated module health/stats diagnostics.
The bot currently registers:
/check: manual oil refresh./health: oil runtime health snapshot./stats: oil session counters./randomroute: generate and post ROTD now./plane: search airplane models by name./airport: airport lookup by IATA (and by ID when enabled)./research: research demand/relationship between two airport codes./crash_stats: crash handler stats (admin)./system_health: aggregated health across modules (admin)./system_stats: aggregated stats across modules (admin).
Note: when AVIATION_AIRPORT_ID_LOOKUP_ENABLED=true, /airport supports airport_id and code (IATA). Otherwise it supports code (IATA) only.
- Python 3.10+
- Discord bot token
- Discord server/channel IDs
- Clone and enter repo
git clone <repo-url>
cd MfcOilAlert- Create and activate venv
python -m venv venv
.\venv\Scripts\Activate.ps1- Install dependencies
pip install -r requirements.txt- Configure environment
Copy-Item env.example .envThen fill .env with your values.
Recommended settings for production in .env:
RUN_SUPERVISED=truefor automatic recovery.CLEAR_GUILD_COMMANDS_ON_STARTUP=falseto avoid deleting guild commands on each restart.ROTD_SCHEDULE_ENABLED=trueonly if you want daily automatic ROTD posts.AVIATION_AIRPORT_ID_LOOKUP_ENABLED=falseunless your team needs ID-based airport lookups.- Keep
ROTD_ORIGIN_IDandROTD_DEST_IDempty for random-route behavior. - Set
BOT_STATUS=Monitoring Oil Prices(or another stable production status text).
Current content from env.example:
# Discord
DISCORD_TOKEN=
DISCORD_OIL_CHANNEL=
DISCORD_RROTD_CHANNEL=
# Bot Runtime
BOT_STATUS=Local Test Mode
CLEAR_GUILD_COMMANDS_ON_STARTUP=true
RUN_SUPERVISED=true
# Oil Service
OIL_PRICE_URL=https://play.myfly.club/oil-prices
POLLING_INTERVAL=180 # 3 minutes in seconds
# Aviation Service
AVIATION_INFO_ENABLED=true
AVIATION_AIRPORT_ID_LOOKUP_ENABLED=false
# ROTD Service
ROTD_ENABLED=true
ROTD_SCHEDULE_ENABLED=true
ROTD_SCHEDULE_TZ=UTC
ROTD_SCHEDULE_HOUR=15
ROTD_SCHEDULE_MINUTE=0
ROTD_ORIGIN_ID=
ROTD_DEST_ID=
ROTD_MIN_DISTANCE_KM=3000
ROTD_MIN_AIRPORT_SIZE=5
ROTD_DEST_MAX_SIZE_FILTER_ENABLED=false
ROTD_DEST_MAX_SIZE=7
ROTD_MAX_RETRY_ATTEMPTS=100
ROTD_RANDOM_SELECTION_TIMEOUT_SECONDS=300
ROTD_SCHEDULE_SELECTION_TIMEOUT_SECONDS=600
ROTD_SELECTION_SAFETY_FLOOR_ATTEMPTS=200
ROTD_FALLBACK_MAX_AIRPORT_ID=500
# MyFly API
MFC_BASE_URL=https://play.myfly.club
MFC_API_VERSION=v5.1.1
MFC_SEARCH_ROUTE_PATH_TEMPLATE=/search-route/{origin_id}/{dest_id}
MFC_RESEARCH_LINK_PATH_TEMPLATE=/research-link/{origin_id}/{dest_id}
MFC_AIRPORT_BY_ID_PATH_TEMPLATE=/airports/{airport_id}/detail
MFC_AIRPORT_DETAIL_PATH_TEMPLATE=/airports/{airport_id}/detail
MFC_AIRPORT_DETAIL_STATIC_PATH_TEMPLATE=/airports/{airport_id}/detail-static
MFC_AIRPORTS_PATH=/airports
MFC_AIRPORTS_STATIC_PATH=/api/v5.1.1/airports-static
MFC_AIRPLANE_MODELS_PATH=/api/v5.1.1/airplane-models
# HTTP Circuit Breaker
CB_FAILURE_THRESHOLD=3 # consecutive failures before opening breaker
CB_OPEN_SECONDS=120 # cooldown before half-open probe
CB_HALF_OPEN_PROBES=1 # number of probe requests in half-open
# Crash Handler
MAX_RESTART_ATTEMPTS=5
RESTART_DELAY_BASE=10 # Base delay in seconds (exponential backoff)
EMERGENCY_CHANNEL_ID= # Optional: separate channel for crash alerts (leave empty to use main channel)Recommended launcher:
python src\main.pyAlternative launcher (same runtime):
python src\bot.pyRUN_SUPERVISED modes:
true: supervised with restart logic.false: direct run, useful for debugging.
- Start bot.
- In Discord:
- Run
/checkand verify oil embed post. - Run
/randomrouteand verify ROTD post. - Run
/planeand/airport. - Run
/research origin_code:EZE dest_code:JFK(example).
- Run
- Run diagnostics:
/system_health/system_stats
At minimum, bot role needs:
- View Channels
- Send Messages
- Embed Links
- Manage Channels (required for oil channel rename)
If renaming fails, check channel-level permission overrides for explicit deny.
MfcOilAlert/
src/
bot.py
main.py
app/
application.py
bootstrap.py
module_registry.py
runtime.py
modules/
oil/
module.py
rotd/
module.py
aviation_info/
module.py
ops/
module.py
shared/
formatting.py
module_contract.py
config/
config.py
utils/
aviation_info_service.py
bot_supervisor.py
crash_handler.py
discord_client_wrapper.py
health_status.py
http_client.py
mfc_api.py
price_monitor.py
price_parser.py
rotd_formatter.py
rotd_service.py
env.example
requirements.txt
src/bot.pyis thin and delegates lifecycle to modular runtime/application services.app/bootstrap.pywires dependencies and module registration.- Feature behavior is encapsulated in
modules/*. - Reusable technical services live in
utils/*.
- Verify corresponding feature flag in
.env:/randomrouterequiresROTD_ENABLED=true./plane,/airport,/researchrequireAVIATION_INFO_ENABLED=true.
- Restart bot after changing
.env. - Check startup logs for command sync.
- Set
AVIATION_AIRPORT_ID_LOOKUP_ENABLED=trueand restart.
- Check logs for exceptions in command handlers.
- Verify channel IDs and permissions.
- Ensure bot has
Manage Channelsin role and channel overrides.
- Do not commit
.env. - Rotate
DISCORD_TOKENif it was ever exposed.