ctfy.sdk.admin_resources.competition_invites
client.admin.competition_invites — private-competition
participation allowlist (admin).
1"""``client.admin.competition_invites`` — private-competition 2participation allowlist (admin).""" 3 4from __future__ import annotations 5 6import builtins 7 8from ctfy.sdk._helpers import _extract_items, _raise_for_status 9from ctfy.sdk.base import BaseHttpClient 10from ctfy.server.models import BulkInviteResponse, CompetitionInviteInfo 11 12 13class AdminCompetitionInvitesResource: 14 """Invite / revoke users on a private competition's participation 15 allowlist. Managed by any competition admin (global or per-comp).""" 16 17 def __init__(self, http: BaseHttpClient) -> None: 18 self._http = http 19 20 def list( 21 self, competition_id: str, offset: int = 0, limit: int = 50 22 ) -> builtins.list[CompetitionInviteInfo]: 23 """Everyone invited to participate in one competition.""" 24 resp = self._http.request( 25 "GET", 26 f"/admin/competitions/{competition_id}/invites", 27 params={"offset": offset, "limit": limit}, 28 ) 29 _raise_for_status(resp) 30 return _extract_items(resp.json(), CompetitionInviteInfo) 31 32 def invite( 33 self, competition_id: str, *, user_id: str = "", email: str = "" 34 ) -> CompetitionInviteInfo: 35 """Invite someone to participate. 36 37 Identify the target by ``user_id`` or ``email``. An ``email`` 38 with no account yet is recorded against the address and claimed 39 when its owner first signs in with a verified address — the 40 returned row carries ``pending_signup=True`` in that case. A 41 ``user_id`` that matches nothing is still a 404: an id names an 42 account or nothing. 43 """ 44 resp = self._http.request( 45 "POST", 46 f"/admin/competitions/{competition_id}/invites", 47 json={"user_id": user_id, "email": email}, 48 ) 49 _raise_for_status(resp) 50 return CompetitionInviteInfo.model_validate(resp.json()) 51 52 def invite_bulk(self, competition_id: str, emails: str) -> BulkInviteResponse: 53 """Invite a pasted list of addresses in one call. 54 55 ``emails`` is the raw blob — newline / comma / semicolon 56 separated. Splitting happens server-side so every client agrees 57 on the rules. The response classifies each address as 58 ``invited`` / ``pending`` / ``already`` / ``invalid``. 59 """ 60 resp = self._http.request( 61 "POST", 62 f"/admin/competitions/{competition_id}/invites/bulk", 63 json={"emails": emails}, 64 ) 65 _raise_for_status(resp) 66 return BulkInviteResponse.model_validate(resp.json()) 67 68 def revoke(self, competition_id: str, user_id: str) -> None: 69 """Remove a user from the participation allowlist.""" 70 resp = self._http.request( 71 "DELETE", f"/admin/competitions/{competition_id}/invites/{user_id}" 72 ) 73 _raise_for_status(resp) 74 75 def revoke_email(self, competition_id: str, email: str) -> None: 76 """Withdraw an invitation still waiting for its person.""" 77 resp = self._http.request( 78 "DELETE", f"/admin/competitions/{competition_id}/invites/by-email/{email}" 79 ) 80 _raise_for_status(resp)
14class AdminCompetitionInvitesResource: 15 """Invite / revoke users on a private competition's participation 16 allowlist. Managed by any competition admin (global or per-comp).""" 17 18 def __init__(self, http: BaseHttpClient) -> None: 19 self._http = http 20 21 def list( 22 self, competition_id: str, offset: int = 0, limit: int = 50 23 ) -> builtins.list[CompetitionInviteInfo]: 24 """Everyone invited to participate in one competition.""" 25 resp = self._http.request( 26 "GET", 27 f"/admin/competitions/{competition_id}/invites", 28 params={"offset": offset, "limit": limit}, 29 ) 30 _raise_for_status(resp) 31 return _extract_items(resp.json(), CompetitionInviteInfo) 32 33 def invite( 34 self, competition_id: str, *, user_id: str = "", email: str = "" 35 ) -> CompetitionInviteInfo: 36 """Invite someone to participate. 37 38 Identify the target by ``user_id`` or ``email``. An ``email`` 39 with no account yet is recorded against the address and claimed 40 when its owner first signs in with a verified address — the 41 returned row carries ``pending_signup=True`` in that case. A 42 ``user_id`` that matches nothing is still a 404: an id names an 43 account or nothing. 44 """ 45 resp = self._http.request( 46 "POST", 47 f"/admin/competitions/{competition_id}/invites", 48 json={"user_id": user_id, "email": email}, 49 ) 50 _raise_for_status(resp) 51 return CompetitionInviteInfo.model_validate(resp.json()) 52 53 def invite_bulk(self, competition_id: str, emails: str) -> BulkInviteResponse: 54 """Invite a pasted list of addresses in one call. 55 56 ``emails`` is the raw blob — newline / comma / semicolon 57 separated. Splitting happens server-side so every client agrees 58 on the rules. The response classifies each address as 59 ``invited`` / ``pending`` / ``already`` / ``invalid``. 60 """ 61 resp = self._http.request( 62 "POST", 63 f"/admin/competitions/{competition_id}/invites/bulk", 64 json={"emails": emails}, 65 ) 66 _raise_for_status(resp) 67 return BulkInviteResponse.model_validate(resp.json()) 68 69 def revoke(self, competition_id: str, user_id: str) -> None: 70 """Remove a user from the participation allowlist.""" 71 resp = self._http.request( 72 "DELETE", f"/admin/competitions/{competition_id}/invites/{user_id}" 73 ) 74 _raise_for_status(resp) 75 76 def revoke_email(self, competition_id: str, email: str) -> None: 77 """Withdraw an invitation still waiting for its person.""" 78 resp = self._http.request( 79 "DELETE", f"/admin/competitions/{competition_id}/invites/by-email/{email}" 80 ) 81 _raise_for_status(resp)
Invite / revoke users on a private competition's participation allowlist. Managed by any competition admin (global or per-comp).
21 def list( 22 self, competition_id: str, offset: int = 0, limit: int = 50 23 ) -> builtins.list[CompetitionInviteInfo]: 24 """Everyone invited to participate in one competition.""" 25 resp = self._http.request( 26 "GET", 27 f"/admin/competitions/{competition_id}/invites", 28 params={"offset": offset, "limit": limit}, 29 ) 30 _raise_for_status(resp) 31 return _extract_items(resp.json(), CompetitionInviteInfo)
Everyone invited to participate in one competition.
33 def invite( 34 self, competition_id: str, *, user_id: str = "", email: str = "" 35 ) -> CompetitionInviteInfo: 36 """Invite someone to participate. 37 38 Identify the target by ``user_id`` or ``email``. An ``email`` 39 with no account yet is recorded against the address and claimed 40 when its owner first signs in with a verified address — the 41 returned row carries ``pending_signup=True`` in that case. A 42 ``user_id`` that matches nothing is still a 404: an id names an 43 account or nothing. 44 """ 45 resp = self._http.request( 46 "POST", 47 f"/admin/competitions/{competition_id}/invites", 48 json={"user_id": user_id, "email": email}, 49 ) 50 _raise_for_status(resp) 51 return CompetitionInviteInfo.model_validate(resp.json())
Invite someone to participate.
Identify the target by user_id or email. An email
with no account yet is recorded against the address and claimed
when its owner first signs in with a verified address — the
returned row carries pending_signup=True in that case. A
user_id that matches nothing is still a 404: an id names an
account or nothing.
53 def invite_bulk(self, competition_id: str, emails: str) -> BulkInviteResponse: 54 """Invite a pasted list of addresses in one call. 55 56 ``emails`` is the raw blob — newline / comma / semicolon 57 separated. Splitting happens server-side so every client agrees 58 on the rules. The response classifies each address as 59 ``invited`` / ``pending`` / ``already`` / ``invalid``. 60 """ 61 resp = self._http.request( 62 "POST", 63 f"/admin/competitions/{competition_id}/invites/bulk", 64 json={"emails": emails}, 65 ) 66 _raise_for_status(resp) 67 return BulkInviteResponse.model_validate(resp.json())
Invite a pasted list of addresses in one call.
emails is the raw blob — newline / comma / semicolon
separated. Splitting happens server-side so every client agrees
on the rules. The response classifies each address as
invited / pending / already / invalid.
69 def revoke(self, competition_id: str, user_id: str) -> None: 70 """Remove a user from the participation allowlist.""" 71 resp = self._http.request( 72 "DELETE", f"/admin/competitions/{competition_id}/invites/{user_id}" 73 ) 74 _raise_for_status(resp)
Remove a user from the participation allowlist.
76 def revoke_email(self, competition_id: str, email: str) -> None: 77 """Withdraw an invitation still waiting for its person.""" 78 resp = self._http.request( 79 "DELETE", f"/admin/competitions/{competition_id}/invites/by-email/{email}" 80 ) 81 _raise_for_status(resp)
Withdraw an invitation still waiting for its person.