""" Background tasks for Castle accounting extension. These tasks handle automated reconciliation checks and maintenance. """ import asyncio from datetime import datetime from typing import Optional from lnbits.tasks import register_invoice_listener from .crud import check_balance_assertion, get_balance_assertions from .models import AssertionStatus async def check_all_balance_assertions() -> dict: """ Check all balance assertions and return results. This can be called manually or scheduled to run daily. Returns: dict: Summary of check results """ from lnbits.helpers import urlsafe_short_hash # Get all assertions all_assertions = await get_balance_assertions(limit=1000) results = { "task_id": urlsafe_short_hash(), "timestamp": datetime.now().isoformat(), "total": len(all_assertions), "checked": 0, "passed": 0, "failed": 0, "errors": 0, "failed_assertions": [], } for assertion in all_assertions: try: checked = await check_balance_assertion(assertion.id) results["checked"] += 1 if checked.status == AssertionStatus.PASSED: results["passed"] += 1 elif checked.status == AssertionStatus.FAILED: results["failed"] += 1 results["failed_assertions"].append({ "id": assertion.id, "account_id": assertion.account_id, "expected_sats": assertion.expected_balance_sats, "actual_sats": checked.checked_balance_sats, "difference_sats": checked.difference_sats, }) except Exception as e: results["errors"] += 1 print(f"Error checking assertion {assertion.id}: {e}") # Log results if results["failed"] > 0: print(f"[CASTLE] Daily reconciliation check: {results['failed']} FAILED assertions!") for failed in results["failed_assertions"]: print(f" - Account {failed['account_id']}: expected {failed['expected_sats']}, got {failed['actual_sats']}") else: print(f"[CASTLE] Daily reconciliation check: All {results['passed']} assertions passed ✓") return results async def scheduled_daily_reconciliation(): """ Scheduled task that runs daily to check all balance assertions. This function is meant to be called by a scheduler (cron, systemd timer, etc.) or by LNbits background task system. """ print(f"[CASTLE] Running scheduled daily reconciliation check at {datetime.now()}") try: results = await check_all_balance_assertions() # TODO: Send notifications if there are failures # This could send email, webhook, or in-app notification if results["failed"] > 0: print(f"[CASTLE] WARNING: {results['failed']} balance assertions failed!") # Future: Send alert notification return results except Exception as e: print(f"[CASTLE] Error in scheduled reconciliation: {e}") raise def start_daily_reconciliation_task(): """ Initialize the daily reconciliation task. This can be called from the extension's __init__.py or configured to run via external cron job. For cron setup: # Run daily at 2 AM 0 2 * * * curl -X POST http://localhost:5000/castle/api/v1/tasks/daily-reconciliation -H "X-Api-Key: YOUR_ADMIN_KEY" """ print("[CASTLE] Daily reconciliation task registered") # In a production system, you would register this with LNbits task scheduler # For now, it can be triggered manually via API endpoint