sw1tch/cleanup.py

133 lines
4.7 KiB
Python

import os
import json
import yaml
import httpx
import logging
from typing import List, Dict
from datetime import datetime, timedelta
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Load paths and config
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
REGISTRATIONS_PATH = os.path.join(BASE_DIR, "registrations.json")
def load_config() -> dict:
"""Load configuration from yaml file."""
with open(CONFIG_PATH, "r") as f:
return yaml.safe_load(f)
def load_registrations() -> List[Dict]:
"""Load current registrations from JSON file."""
try:
with open(REGISTRATIONS_PATH, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_registrations(registrations: List[Dict]):
"""Save updated registrations back to JSON file."""
with open(REGISTRATIONS_PATH, "w") as f:
json.dump(registrations, f, indent=2)
async def check_username_exists(username: str, homeserver: str) -> bool:
"""
Check if a username exists on the Matrix server.
Returns True if the username exists, False otherwise.
"""
url = f"https://{homeserver}/_matrix/client/v3/register/available?username={username}"
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, timeout=5)
if response.status_code == 200:
# 200 OK with available=true means username exists
return response.json().get("available", False)
elif response.status_code == 400:
# 400 Bad Request means username is not available
return False
except httpx.RequestError as ex:
logger.error(f"Error checking username {username}: {ex}")
return False
return False
async def cleanup_registrations(min_age_hours: int = 24):
"""
Clean up registrations by removing entries for usernames that don't exist on the server,
but only if they're older than min_age_hours.
Never removes entries for existing Matrix users regardless of age.
"""
config = load_config()
registrations = load_registrations()
if not registrations:
logger.info("No registrations found to clean up")
return
logger.info(f"Starting cleanup of {len(registrations)} registrations")
logger.info(f"Will only remove non-existent users registered more than {min_age_hours} hours ago")
# Track which entries to keep
entries_to_keep = []
removed_count = 0
too_new_count = 0
exists_count = 0
current_time = datetime.utcnow()
for entry in registrations:
username = entry["requested_name"]
reg_date = datetime.fromisoformat(entry["datetime"])
age = current_time - reg_date
# First check if the user exists on Matrix
exists = await check_username_exists(username, config["homeserver"])
if exists:
# Always keep entries for existing Matrix users
entries_to_keep.append(entry)
exists_count += 1
logger.info(f"Keeping registration for existing user: {username}")
continue
# For non-existent users, check if they're old enough to remove
if age < timedelta(hours=min_age_hours):
# Keep young entries even if user doesn't exist yet
entries_to_keep.append(entry)
too_new_count += 1
logger.info(f"Keeping recent registration: {username} (age: {age.total_seconds()/3600:.1f} hours)")
else:
# Remove old entries where user doesn't exist
logger.info(f"Removing old registration: {username} (age: {age.total_seconds()/3600:.1f} hours)")
removed_count += 1
# Save updated registrations
save_registrations(entries_to_keep)
logger.info(f"Cleanup complete:")
logger.info(f"- Kept {exists_count} entries for existing Matrix users")
logger.info(f"- Kept {too_new_count} entries younger than {min_age_hours} hours")
logger.info(f"- Removed {removed_count} old entries for non-existent users")
logger.info(f"- Total remaining entries: {len(entries_to_keep)}")
if __name__ == "__main__":
import asyncio
import argparse
parser = argparse.ArgumentParser(description="Clean up Matrix registration entries")
parser.add_argument(
"--min-age-hours",
type=int,
default=24,
help="Minimum age in hours before removing non-existent users (default: 24)"
)
args = parser.parse_args()
asyncio.run(cleanup_registrations(args.min_age_hours))