Telegram bot that monitors your Bitcoin NerdMiners on Public-Pool and sends statistics and smart alerts to a Telegram group.
- Miner monitoring: Hashrate (instant + 24h average), best difficulty (session + all-time), uptime, online/offline status
- Pool statistics: Total hashrate, miner count, your contribution percentage
- Bitcoin network stats: Current block height, difficulty, network hashrate
- Smart alerts: Disconnection detected, low hashrate (vs 24h average), new personal records, new/disappeared miners, pool block found
- TOP BD: Tracks the top 10 best difficulties ever achieved across all sessions, displaying the top 5 by default in the stats message
- Auto-pinned stats message: A single stats message is kept pinned and updated in the group; pin notification messages are automatically deleted to keep the chat clean
- Worker identification: Automatically handles multiple miners with the same API name (e.g., old NerdMiners that all report as "worker" without customization options)
- SQLite storage: Efficient 90-day rolling history for hashrate averaging and session tracking (WAL mode for reliability)
- Automatic backups: Database backups every 24 hours with 30-day retention
- Python 3.10 or higher
- pip (Python package manager)
- A Telegram account
- A Bitcoin address mining on public-pool.io or auto-hosted Public-Pool
Follow these steps carefully to create and configure your Telegram bot.
- Open Telegram and search for @BotFather
- Send the command
/newbot - Choose a name for your bot (e.g., "NerdMiners Monitor")
- Choose a username for your bot (must end in
bot, e.g.,my_nerdminers_bot) - BotFather will give you a Bot Token — save it, you'll need it later
Official documentation: Telegram Bot API - BotFather
- Open Telegram and create a new group
- Give it a name (e.g., "NerdMiners Monitoring")
- Telegram requires you to add at least one member (you can remove them later)
- Open the group settings
- Select Add Members
- Search for your bot by its username
- Add the bot to the group
- Go to group settings → Administrators → Add Administrator
- Select your bot and enable at least these permissions:
- Send Messages
- Delete Messages ← Required for deleting old stats messages and pin notification cleanup
- Pin Messages ← Required for the bot to keep the stats message pinned at the top of the group
The bot needs the group's Chat ID to know where to send messages.
Method 1 — Using message link (recommended):
- Send any message in the group (e.g., "hello")
- Right-click the message and select "Copy Message Link"
- Paste the URL, which will have a structure similar to this:
https://t.me/c/3892682082/1
- Your group ID will be the first group of numbers, and you must add
-100to it- According to this example:
-100+3892682082=-1003892682082
- According to this example:
This is an important security step. After adding the bot to your group:
- Open @BotFather again
- Send
/mybots - Select your bot
- Go to Bot Settings → Allow Groups?
- Select Disable
This prevents anyone else from adding your bot to other groups. The bot will continue to work in groups where it's already a member.
Important: The bot ignores all direct messages. It only operates in the configured Telegram group.
git clone <repository-url>
cd NerdMiners_Public_Pool_StatsRun the setup script:
chmod +x First_Setup.sh
./First_Setup.shThe script will create .env from the .env.example template on first run.
nano .envSet all three variables:
BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
CHAT_ID=-1001234567890
BTC_ADDRESS=bc1q...
Then run the setup script again. The configuration script will verify that all variables are set and will begin the bot environment setup process, automatically installing the necessary dependencies. If you're missing any program like Python or pip, it will notify you and show you the command to install it.
./First_Setup.shEdit config.py to configure custom names for your miners and how they will be displayed in Telegram messages.
This way you can assign a more descriptive name to your miner related to the worker name that appears in Public-Pool.
Keep in mind that you should assign a different name to each worker in its configuration.
NAME_SUBSTITUTIONS = '{"nerdoctaxe_1": "NerdMiner Octaxe Gamma Home", "nerdoctaxe_2": "NerdMiner Octaxe Gamma Work", "worker": "NerdMiner v2 Living Room", "worker_2": "NerdMiner v2 Office"}'Important: The value must be a single-line JSON string — do not split it across lines. This format allows the auto-update system to preserve your names during upgrades. For old NerdMiners that all report as
workerin the API, the bot assigns incremental IDs (worker_1,worker_2, ...). Run the bot once and check the log to discover assigned IDs.
The bot is designed to run periodically via cron — it is not a continuously running service. Each execution fetches the latest data, updates the pinned stats message, sends any alerts, and exits.
At the end of the First_Setup.sh configuration script, you will be shown a command that you must execute to create the cron entry in the system's crontab.
Important — Execution frequency:
- Recommended frequency: every 30 minutes (
*/30 * * * *).- Running more frequently (e.g., every 5 or 10 minutes) is not recommended when using public-pool.io, as its API may apply rate limits that could temporarily block your requests.
- If you are using a self-hosted public-pool instance, there are no rate limits — you may run the bot as frequently as you like.
- Running less frequently (e.g., every hour) is perfectly fine and will still provide useful monitoring.
All sensitive values are in .env:
| Variable | Description |
|---|---|
BOT_TOKEN |
Telegram Bot Token from @BotFather |
CHAT_ID |
Telegram group Chat ID (negative number) |
BTC_ADDRESS |
Your Bitcoin mining address on public-pool.io |
Tunable settings are in config.py:
| Setting | Description | Default |
|---|---|---|
AUTO_UPDATE |
Enable automatic updates from the GitHub repository. False disables all auto-updates; you must update manually via git |
True |
API_BASE_URL |
API base URL. Pre-configured for public-pool.io. Self-hosted instances use a different URL and port (e.g., http://umbrel.local:3334/api). See Self-hosted public-pool below |
https://public-pool.io:40557/api |
OFFLINE_TIMEOUT_MINUTES |
Minutes of inactivity before a miner is considered offline | 5 |
HASHRATE_DROP_PERCENT |
Hashrate drop vs 24h average to trigger alert | 30 |
HASHRATE_ALERT_STRIKES |
Consecutive runs with a hashrate drop required before alerting. With a 30-min cron, 2 = drop must persist ≥30 min |
2 |
HASHRATE_ALERT_COOLDOWN_HOURS |
Hours before resending a LOW HASHRATE alert for the same miner. Resets automatically when hashrate recovers | 4 |
NOTIFY_SESSION_BD_RECORD |
False: alert only when a miner beats their all-time best difficulty. True: alert on every new session best, even if it doesn't beat the all-time record |
False |
SHOW_TOP_BD |
Top BDs shown on Telegram | 5 |
MESSAGE_EDIT_LIMIT_HOURS |
Hours before the stats message is recreated (see note below) | 45 |
DATA_RETENTION_DAYS |
Days to keep hashrate history in the database | 90 |
BACKUP_RETENTION_DAYS |
Days to keep database backups | 30 |
NAME_SUBSTITUTIONS |
Custom display names for miners (single-line JSON string) | '{}' |
LOG_LEVEL |
Log verbosity: DEBUG, INFO, WARNING, ERROR |
WARNING |
The bot keeps a single pinned message in Telegram that it edits on each run.
When the message reaches the age of MESSAGE_EDIT_LIMIT_HOURS, the bot deletes it and sends a new one (which is then pinned automatically).
Important: Telegram imposes a 48-hour limit for bots — messages older than 48 hours cannot be edited or deleted via the Bot API. The default value of 45 hours provides a safe 3-hour margin. Do not set this value above 45, or the bot may be unable to delete the old message, resulting in duplicate pinned messages in the group.
| Alert | Trigger |
|---|---|
| DISCONNECTION DETECTED | Miner's session ID changed (new startTime). Includes previous session duration, estimated downtime, and reconnection time |
| MINER OFFLINE | No activity for more than OFFLINE_TIMEOUT_MINUTES minutes |
| LOW HASHRATE | Hashrate dropped more than HASHRATE_DROP_PERCENT% below the 24h average for HASHRATE_ALERT_STRIKES consecutive runs. Cooldown of HASHRATE_ALERT_COOLDOWN_HOURSh between alerts; resets on recovery |
| NEW PERSONAL RECORD | Miner beat their all-time best difficulty (default). Set NOTIFY_SESSION_BD_RECORD = True to also alert on session bests that don't beat the all-time record |
| NEW MINER DETECTED | A previously unknown miner appeared |
| MINER DISAPPEARED | A known miner is no longer visible in the pool |
| YOUR MINER FOUND A BLOCK | One of YOUR miners found a Bitcoin block (matched by your BTC_ADDRESS) |
| BLOCK FOUND BY THE POOL | Another miner on public-pool.io found a Bitcoin block |
- Auto-update: Checks the remote repository for new versions and applies them automatically, preserving your configuration; sends a Telegram notification listing all commits applied since the last update
- Database init: Creates SQLite tables if they don't exist
- Backup: Creates a timestamped copy of the database (skipped if one less than 24h old exists)
- Fetch data: Queries the public-pool.io API for your miners, pool stats, and network stats
- Identify workers: Maps API workers to stable internal IDs (handles duplicate names)
- Check alerts: Compares current state against saved state, detects changes, records sessions
- Send alerts: Any triggered alerts are sent as individual messages to the group
- Update stats: Builds the stats message, edits the existing pinned message (or creates a new one if too old)
- Purge: Deletes hashrate samples older than
DATA_RETENTION_DAYS
NerdMiners_Public_Pool_Stats/
├── .env # Secrets: BOT_TOKEN, CHAT_ID, BTC_ADDRESS (not in git)
├── .env.example # Template for .env
├── config.py # Tunable bot settings and worker name substitutions
├── database.py # SQLite persistence layer (WAL mode, foreign keys)
├── NerdMiners_Bot.py # Main bot script (entry point)
├── First_Setup.sh # First-time setup script
├── Update.sh # Auto-update script (called by NerdMiners_Bot.py on each run)
├── requirements.txt # Python dependencies
├── DB.db # SQLite database (auto-generated)
├── Logs/ # Log files directory (auto-generated)
│ └── NerdMiners_Public_Pool_Stats_Bot.log
└── Backup/ # Database backups (auto-generated, 30-day retention)
└── NerdMiners_Public_Pool_Stats_MMDDYYYY_HHMMSS.db
Base URL: https://public-pool.io:40557/api
| Endpoint | Description |
|---|---|
/api/client/{address} |
Workers for a Bitcoin address (hashrate, best diff, sessions) |
/api/pool |
Pool-wide statistics (total hashrate, miners, blocks found) |
/api/network |
Bitcoin network statistics (block height, difficulty, hashrate) |
This bot is compatible with self-hosted public-pool instances. public-pool is the open-source software behind public-pool.io — anyone can run their own private instance, including via a one-click install on Umbrel.
The bot comes pre-configured for public-pool.io. To use your own instance, change API_BASE_URL in config.py to point to your server's IP or hostname and its API port:
# Self-hosted example (Umbrel, default port 3334)
API_BASE_URL = "http://umbrel.local:3334/api"The port depends on your installation — check the network settings of your public-pool instance.
