Added support to query pool mining statistics every X minutes.

This commit is contained in:
JustinOros
2025-11-26 09:27:45 -07:00
parent 6e5fcddc64
commit fb422d152a
+152 -1
View File
@@ -12,11 +12,14 @@ import time
import signal import signal
import platform import platform
import urllib.request import urllib.request
import urllib.error
import tarfile import tarfile
import zipfile import zipfile
import re import re
import threading
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from datetime import datetime
class MoneroMiner: class MoneroMiner:
def __init__(self, config_file: str = "miner-monero.json"): def __init__(self, config_file: str = "miner-monero.json"):
@@ -24,6 +27,8 @@ class MoneroMiner:
self.script_dir = os.path.dirname(os.path.abspath(__file__)) self.script_dir = os.path.dirname(os.path.abspath(__file__))
self.config = None self.config = None
self.process: Optional[subprocess.Popen] = None self.process: Optional[subprocess.Popen] = None
self.stats_thread = None
self.stop_stats = False
def detect_os(self) -> str: def detect_os(self) -> str:
"""Detect the operating system""" """Detect the operating system"""
@@ -487,7 +492,8 @@ class MoneroMiner:
"extra_args": [], "extra_args": [],
"log_file": "monero-miner.log", "log_file": "monero-miner.log",
"api_port": 0, "api_port": 0,
"donate_level": 1 "donate_level": 1,
"stats_update_interval": 300
} }
with open(self.config_file, 'w') as f: with open(self.config_file, 'w') as f:
@@ -529,6 +535,145 @@ class MoneroMiner:
if config['mining_mode'] == 'pool' and 'pool_address' not in config: if config['mining_mode'] == 'pool' and 'pool_address' not in config:
raise ValueError("pool_address is required for pool mining") raise ValueError("pool_address is required for pool mining")
def is_supportxmr_pool(self) -> bool:
"""Check if the configured pool is SupportXMR"""
if self.config['mining_mode'] != 'pool':
return False
pool_addr = self.config.get('pool_address', '').lower()
return 'supportxmr.com' in pool_addr
def fetch_pool_stats(self) -> Optional[Dict]:
"""Fetch mining statistics from SupportXMR API"""
try:
wallet = self.config['wallet_address']
url = f"https://www.supportxmr.com/api/miner/{wallet}/stats"
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0')
with urllib.request.urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode())
return data
except urllib.error.HTTPError as e:
print(f"\nError fetching pool stats (HTTP {e.code}): {e.reason}")
return None
except urllib.error.URLError as e:
print(f"\nError fetching pool stats (Network): {e.reason}")
return None
except Exception as e:
print(f"\nError fetching pool stats: {e}")
return None
def format_hashrate(self, hash_rate: float) -> str:
"""Format hash rate in human-readable format"""
if hash_rate >= 1000000:
return f"{hash_rate / 1000000:.2f} MH/s"
elif hash_rate >= 1000:
return f"{hash_rate / 1000:.2f} KH/s"
else:
return f"{hash_rate:.2f} H/s"
def format_xmr(self, piconero: int) -> str:
"""Convert piconero to XMR"""
xmr = piconero / 1000000000000
return f"{xmr:.12f} XMR"
def format_time_ago(self, timestamp: int) -> str:
"""Format timestamp as time ago"""
now = int(time.time())
diff = now - timestamp
if diff < 60:
return f"{diff} seconds ago"
elif diff < 3600:
return f"{diff // 60} minutes ago"
elif diff < 86400:
return f"{diff // 3600} hours ago"
else:
return f"{diff // 86400} days ago"
def display_pool_stats(self, stats: Dict):
"""Display pool statistics in a readable format"""
print("\n" + "="*60)
print(f"Pool Statistics - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("="*60)
hash_rate = stats.get('hash', 0)
total_hashes = stats.get('totalHashes', 0)
valid_shares = stats.get('validShares', 0)
invalid_shares = stats.get('invalidShares', 0)
amt_paid = stats.get('amtPaid', 0)
amt_due = stats.get('amtDue', 0)
txn_count = stats.get('txnCount', 0)
last_hash = stats.get('lastHash', 0)
print(f"Current Hashrate: {self.format_hashrate(hash_rate)}")
print(f"Total Hashes: {total_hashes:,}")
print(f"Valid Shares: {valid_shares:,}")
print(f"Invalid Shares: {invalid_shares:,}")
if valid_shares + invalid_shares > 0:
efficiency = (valid_shares / (valid_shares + invalid_shares)) * 100
print(f"Share Efficiency: {efficiency:.2f}%")
print(f"\nAmount Paid: {self.format_xmr(amt_paid)}")
print(f"Amount Due: {self.format_xmr(amt_due)}")
print(f"Total Earned: {self.format_xmr(amt_paid + amt_due)}")
print(f"Payment Count: {txn_count}")
if last_hash > 0:
print(f"Last Share: {self.format_time_ago(last_hash)}")
print("="*60 + "\n")
def stats_monitor_thread(self):
"""Background thread to periodically fetch and display pool stats"""
interval = self.config.get('stats_update_interval', 300)
# Display stats immediately
print("\nFetching initial pool statistics...")
stats = self.fetch_pool_stats()
if stats:
self.display_pool_stats(stats)
else:
print("Could not fetch initial pool statistics. Will retry...\n")
# Continue periodic updates
while not self.stop_stats:
# Wait for the interval, checking stop flag frequently
for _ in range(interval):
if self.stop_stats:
break
time.sleep(1)
if self.stop_stats:
break
# Fetch and display stats
stats = self.fetch_pool_stats()
if stats:
self.display_pool_stats(stats)
def start_stats_monitor(self):
"""Start the stats monitoring thread"""
if not self.is_supportxmr_pool():
return
print("\nPool stats monitoring enabled for SupportXMR")
interval = self.config.get('stats_update_interval', 300)
print(f"Stats will be updated every {interval} seconds ({interval // 60} minutes)")
self.stop_stats = False
self.stats_thread = threading.Thread(target=self.stats_monitor_thread, daemon=True)
self.stats_thread.start()
def stop_stats_monitor(self):
"""Stop the stats monitoring thread"""
if self.stats_thread:
self.stop_stats = True
self.stats_thread.join(timeout=2)
def initialize(self): def initialize(self):
"""Initialize the miner - check for xmrig and setup config""" """Initialize the miner - check for xmrig and setup config"""
# Check if xmrig is installed # Check if xmrig is installed
@@ -676,6 +821,9 @@ class MoneroMiner:
print(f"Command: {' '.join(cmd)}") print(f"Command: {' '.join(cmd)}")
print("="*60 + "\n") print("="*60 + "\n")
# Start stats monitoring if using SupportXMR
self.start_stats_monitor()
# Setup logging if specified # Setup logging if specified
log_file = self.config.get('log_file') log_file = self.config.get('log_file')
log_handle = None log_handle = None
@@ -722,9 +870,12 @@ class MoneroMiner:
finally: finally:
if log_handle: if log_handle:
log_handle.close() log_handle.close()
self.stop_stats_monitor()
def stop_mining(self): def stop_mining(self):
"""Stop the mining process""" """Stop the mining process"""
self.stop_stats_monitor()
if self.process: if self.process:
print("Stopping miner...") print("Stopping miner...")
self.process.terminate() self.process.terminate()