- Add get_cached_ip() with 5-minute TTL to bot service; replaces raw
curl to api.ipify.org in tg_send(), /mp_link, /mp_add, /mp_rotate
- Escape server label with _esc() in tg_send() and tg_send_photo()
headers so underscores/asterisks don't break Telegram Markdown
- Escape label with _esc() in all error message strings across handlers
- Refactor update_traffic() to fetch /metrics once and pass raw data
to get_user_stats_tg() — eliminates one curl per user per interval
- get_user_stats_tg() accepts pre-fetched metrics as optional arg
- /mp_secrets fetches metrics once before iterating users
- Fix /mp_remove: check label existence first, then guard against
removing the last secret with a clear dedicated error message
- Implement real iptables/ipset geo-blocking with ipdeny.com CIDR lists
- Tag-based cleanup (mtproxymax-geoblock comment, mtpmax_ ipset prefix)
- Batch ipset loading via restore for fast rule application
- Auto-reapply geo-blocks on proxy start, full cleanup on uninstall
- Fix _repeat() and _strlen() to use pure bash (no subprocesses)
- Cache docker inspect result across main menu renders
- Cache get_public_ip() for 5 minutes, reduce timeout 5s -> 3s
- Parse proxy stats with single read instead of 3 awk subprocesses
- Cache _cached_start_epoch outside render loop
- Add comprehensive README with feature docs, comparison table,
user management recipes, and practical examples