54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
# retoor <retoor@molodetz.nl>
|
|||
|
|
import json
|
||
|
|
import threading
|
||
|
|
import time
|
||
|
|
from datetime import datetime, timedelta, timezone
|
||
|
|
|
||
|
|
from molodetz import config
|
||
|
|
from molodetz.database import db, get_service_state, now_iso, update_user
|
||
|
|
|
||
|
|
_lock = threading.Lock()
|
||
|
|
_last_write = {}
|
||
|
|
|
||
|
|
|
||
|
|
def touch(uid):
|
||
|
|
if not uid:
|
||
|
|
return
|
||
|
|
now = time.monotonic()
|
||
|
|
throttle = max(1, config.PRESENCE_TIMEOUT_SECONDS // 2)
|
||
|
|
with _lock:
|
||
|
|
if now - _last_write.get(uid, 0) < throttle:
|
||
|
|
return
|
||
|
|
_last_write[uid] = now
|
||
|
|
update_user(uid, last_seen_at=now_iso())
|
||
|
|
|
||
|
|
|
||
|
|
def compute_roster():
|
||
|
|
if "users" not in db.tables:
|
||
|
|
return []
|
||
|
|
window = config.PRESENCE_TIMEOUT_SECONDS + config.PRESENCE_ONLINE_MARGIN_SECONDS
|
||
|
|
cutoff = (datetime.now(timezone.utc) - timedelta(seconds=window)).isoformat()
|
||
|
|
rows = db.query(
|
||
|
|
"SELECT uid FROM users WHERE last_seen_at >= :cutoff AND deleted_at IS NULL "
|
||
|
|
"ORDER BY last_seen_at DESC LIMIT :limit",
|
||
|
|
cutoff=cutoff,
|
||
|
|
limit=config.PRESENCE_TRACK_LIMIT,
|
||
|
|
)
|
||
|
|
return [row["uid"] for row in rows]
|
||
|
|
|
||
|
|
|
||
|
|
def published_roster():
|
||
|
|
row = get_service_state("presence")
|
||
|
|
if row and row.get("metrics"):
|
||
|
|
try:
|
||
|
|
metrics = json.loads(row["metrics"])
|
||
|
|
except ValueError:
|
||
|
|
metrics = {}
|
||
|
|
if "roster" in metrics:
|
||
|
|
return metrics["roster"]
|
||
|
|
return compute_roster()
|
||
|
|
|
||
|
|
|
||
|
|
def online_uids(limit=None):
|
||
|
|
return published_roster()[: limit or config.PRESENCE_ONLINE_LIMIT]
|