diff --git a/app/main.py b/app/main.py index 1595cc5..4295feb 100644 --- a/app/main.py +++ b/app/main.py @@ -79,6 +79,16 @@ MEMBERS_GROUP_ID = os.environ.get("MEMBERS_GROUP_ID", "") # being deleted) so they lose access but can reclaim their data on reactivation. INACTIVE_GROUP_ID = os.environ.get("INACTIVE_GROUP_ID", "") +# Manually-provisioned members (comma-separated emails) who are NOT in the +# Open Collective member list. Used for the free "founder" tier, which OC +# cannot process as a $0 recurring subscription. These members are exempt from +# the sweep's deactivation (they have no OC membership to lapse). +MANUAL_MEMBERS = { + e.strip().lower() + for e in os.environ.get("MANUAL_MEMBERS", "").split(",") + if e.strip() +} + # Loomio integration (forum.inference.coop). The portal reconciles the Loomio # group to the current active-member list via the User API (/api/b2). LOOMIO_BASE = os.environ.get("LOOMIO_BASE", "https://forum.inference.coop") @@ -793,9 +803,10 @@ async def reconcile_memberships() -> dict: except Exception as e: logger.warning("Sweep: failed to provision %s: %s", email, e) - # 2. Deactivate lapsed members. + # 2. Deactivate lapsed members (but never manual/founder members, who have + # no OC membership to lapse). for email, user_id in provisioned.items(): - if email not in active_emails: + if email not in active_emails and email not in MANUAL_MEMBERS: try: if INACTIVE_GROUP_ID and user_id: await cloudron_set_group(user_id, INACTIVE_GROUP_ID)