64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
# utils/seen_store.py
|
|
import os, redis
|
|
from typing import Iterable, Optional
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
|
|
_redis_client: Optional[redis.Redis] = None
|
|
|
|
def _get_redis_client() -> Optional[redis.Redis]:
|
|
"""Get Redis client with error handling"""
|
|
global _redis_client
|
|
if _redis_client is not None:
|
|
return _redis_client
|
|
|
|
try:
|
|
_redis_client = redis.from_url(REDIS_URL, decode_responses=True)
|
|
# Test the connection
|
|
_redis_client.ping()
|
|
logger.info("✅ Redis connection established")
|
|
return _redis_client
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Redis connection failed: {e}")
|
|
logger.warning("📝 Pagination will be disabled - posts may be duplicated")
|
|
_redis_client = None
|
|
return None
|
|
|
|
def seen_key(username: str) -> str:
|
|
return f"ig:seen:{username}"
|
|
|
|
def mark_seen(username: str, ids: Iterable[str]) -> None:
|
|
"""Mark posts as seen (Redis optional)"""
|
|
if not ids:
|
|
return
|
|
|
|
r = _get_redis_client()
|
|
if r is None:
|
|
return # Skip if Redis is not available
|
|
|
|
try:
|
|
r.sadd(seen_key(username), *ids)
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Failed to mark posts as seen: {e}")
|
|
|
|
def filter_unseen(username: str, ids: Iterable[str]) -> list[str]:
|
|
"""Filter out already seen posts (Redis optional)"""
|
|
r = _get_redis_client()
|
|
if r is None:
|
|
# If Redis is not available, return all IDs (no filtering)
|
|
return list(ids)
|
|
|
|
try:
|
|
# pipeline for speed
|
|
key = seen_key(username)
|
|
pipe = r.pipeline()
|
|
for i in ids:
|
|
pipe.sismember(key, i)
|
|
flags = pipe.execute()
|
|
return [i for i, seen in zip(ids, flags) if not seen]
|
|
except Exception as e:
|
|
logger.warning(f"⚠️ Failed to filter unseen posts: {e}")
|
|
# Fallback: return all IDs if Redis fails
|
|
return list(ids) |