|
67 | 67 | PrivacyLevel, |
68 | 68 | try_enum, |
69 | 69 | VerificationLevel, |
| 70 | + JoinRequestStatus, |
70 | 71 | ContentFilter, |
71 | 72 | NotificationLevel, |
72 | 73 | NSFWLevel, |
|
91 | 92 | from .file import File |
92 | 93 | from .audit_logs import AuditLogEntry |
93 | 94 | from .object import OLDEST_OBJECT, Object |
| 95 | +from .member_verification import JoinRequest, MemberVerification, MemberVerificationFormField |
94 | 96 | from .onboarding import Onboarding |
95 | 97 | from .welcome_screen import WelcomeScreen, WelcomeChannel |
96 | 98 | from .automod import AutoModRule, AutoModTrigger, AutoModRuleAction |
|
136 | 138 | ForumChannel as ForumChannelPayload, |
137 | 139 | ) |
138 | 140 | from .types.integration import IntegrationType |
| 141 | + from .types.member_verification import JoinRequest as JoinRequestPayload |
139 | 142 | from .types.snowflake import SnowflakeList |
140 | 143 | from .types.widget import EditWidgetSettings |
141 | 144 | from .types.audit_log import AuditLogEvent |
@@ -3964,6 +3967,252 @@ async def edit_welcome_screen( |
3964 | 3967 | data = await self._state.http.edit_welcome_screen(self.id, reason=reason, **fields) |
3965 | 3968 | return WelcomeScreen(data=data, guild=self) |
3966 | 3969 |
|
| 3970 | + async def member_verification(self) -> MemberVerification: |
| 3971 | + """|coro| |
| 3972 | +
|
| 3973 | + Fetches the member verification gate for this guild. |
| 3974 | +
|
| 3975 | + .. versionadded:: 2.8 |
| 3976 | +
|
| 3977 | + Raises |
| 3978 | + ------- |
| 3979 | + NotFound |
| 3980 | + The guild does not have member verification enabled. |
| 3981 | + Forbidden |
| 3982 | + You do not have permissions to fetch the member verification. |
| 3983 | + HTTPException |
| 3984 | + Fetching the member verification failed. |
| 3985 | +
|
| 3986 | + Returns |
| 3987 | + -------- |
| 3988 | + :class:`MemberVerification` |
| 3989 | + The member verification that was fetched. |
| 3990 | + """ |
| 3991 | + data = await self._state.http.get_member_verification(self.id) |
| 3992 | + return MemberVerification(data=data, guild=self) |
| 3993 | + |
| 3994 | + async def edit_member_verification( |
| 3995 | + self, |
| 3996 | + *, |
| 3997 | + enabled: bool = MISSING, |
| 3998 | + form_fields: Sequence[MemberVerificationFormField] = MISSING, |
| 3999 | + description: Optional[str] = MISSING, |
| 4000 | + bulk_action: JoinRequestStatus = MISSING, |
| 4001 | + reason: Optional[str] = None, |
| 4002 | + ) -> MemberVerification: |
| 4003 | + """|coro| |
| 4004 | +
|
| 4005 | + Edits the member verification gate for this guild. |
| 4006 | +
|
| 4007 | + You must have :attr:`~Permissions.manage_guild` to do this. |
| 4008 | +
|
| 4009 | + All parameters are optional. |
| 4010 | +
|
| 4011 | + .. versionadded:: 2.8 |
| 4012 | +
|
| 4013 | + Parameters |
| 4014 | + ----------- |
| 4015 | + enabled: :class:`bool` |
| 4016 | + Whether the member verification gate is enabled. |
| 4017 | + form_fields: List[:class:`MemberVerificationFormField`] |
| 4018 | + The questions the user must answer. There can be up to 5 questions. |
| 4019 | +
|
| 4020 | + Using a field type other than :attr:`MemberVerificationFieldType.terms` |
| 4021 | + requires the guild to have the ``MEMBER_VERIFICATION_MANUAL_APPROVAL`` feature. |
| 4022 | + description: Optional[:class:`str`] |
| 4023 | + A description of what the guild is about. Can be up to 300 characters long. |
| 4024 | + bulk_action: :class:`JoinRequestStatus` |
| 4025 | + What to do with the pending join requests when disabling the gate. |
| 4026 | + Only :attr:`JoinRequestStatus.approved` and :attr:`JoinRequestStatus.rejected` |
| 4027 | + can be used. Defaults to approving. |
| 4028 | + reason: Optional[:class:`str`] |
| 4029 | + The reason for editing the member verification. Shows up on the audit log. |
| 4030 | +
|
| 4031 | + Raises |
| 4032 | + ------- |
| 4033 | + ValueError |
| 4034 | + An invalid ``bulk_action`` was passed. |
| 4035 | + Forbidden |
| 4036 | + You do not have permissions to edit the member verification. |
| 4037 | + HTTPException |
| 4038 | + Editing the member verification failed. |
| 4039 | +
|
| 4040 | + Returns |
| 4041 | + -------- |
| 4042 | + :class:`MemberVerification` |
| 4043 | + The newly updated member verification. |
| 4044 | + """ |
| 4045 | + payload: Dict[str, Any] = {} |
| 4046 | + if enabled is not MISSING: |
| 4047 | + payload['enabled'] = enabled |
| 4048 | + if form_fields is not MISSING: |
| 4049 | + payload['form_fields'] = [field.to_dict() for field in form_fields] |
| 4050 | + if description is not MISSING: |
| 4051 | + payload['description'] = description |
| 4052 | + if bulk_action is not MISSING: |
| 4053 | + if bulk_action not in (JoinRequestStatus.approved, JoinRequestStatus.rejected): |
| 4054 | + raise ValueError('bulk_action must be either JoinRequestStatus.approved or JoinRequestStatus.rejected') |
| 4055 | + payload['bulk_action'] = bulk_action.value |
| 4056 | + |
| 4057 | + data = await self._state.http.edit_member_verification(self.id, reason=reason, **payload) |
| 4058 | + return MemberVerification(data=data, guild=self) |
| 4059 | + |
| 4060 | + async def join_requests( |
| 4061 | + self, |
| 4062 | + *, |
| 4063 | + status: JoinRequestStatus = JoinRequestStatus.submitted, |
| 4064 | + limit: Optional[int] = 100, |
| 4065 | + before: SnowflakeTime = MISSING, |
| 4066 | + after: SnowflakeTime = MISSING, |
| 4067 | + oldest_first: bool = MISSING, |
| 4068 | + ) -> AsyncIterator[JoinRequest]: |
| 4069 | + """Retrieves an :term:`asynchronous iterator` of the guild's :class:`JoinRequest`\\s. |
| 4070 | +
|
| 4071 | + You must have :attr:`~Permissions.kick_members` to do this. |
| 4072 | +
|
| 4073 | + .. note:: |
| 4074 | +
|
| 4075 | + Join requests with a status of :attr:`JoinRequestStatus.submitted` are ordered |
| 4076 | + and paginated by the request itself, whereas actioned join requests are ordered |
| 4077 | + and paginated by :attr:`JoinRequest.actioned_at`. |
| 4078 | +
|
| 4079 | + .. versionadded:: 2.8 |
| 4080 | +
|
| 4081 | + Examples |
| 4082 | + --------- |
| 4083 | +
|
| 4084 | + Usage :: |
| 4085 | +
|
| 4086 | + async for request in guild.join_requests(): |
| 4087 | + print(request.user, request.status) |
| 4088 | +
|
| 4089 | + Flattening into a list :: |
| 4090 | +
|
| 4091 | + requests = [request async for request in guild.join_requests()] |
| 4092 | +
|
| 4093 | + Parameters |
| 4094 | + ----------- |
| 4095 | + status: :class:`JoinRequestStatus` |
| 4096 | + The status of the join requests to retrieve. |
| 4097 | + Defaults to :attr:`JoinRequestStatus.submitted`. |
| 4098 | +
|
| 4099 | + :attr:`JoinRequestStatus.started` cannot be used. |
| 4100 | + limit: Optional[:class:`int`] |
| 4101 | + The number of join requests to retrieve. If ``None``, retrieves every |
| 4102 | + join request with the given status. Note that this is potentially slow. |
| 4103 | + before: Union[:class:`abc.Snowflake`, :class:`datetime.datetime`] |
| 4104 | + Retrieve join requests before this date or join request. |
| 4105 | + If a datetime is provided, it is recommended to use a UTC aware datetime. |
| 4106 | + If the datetime is naive, it is assumed to be local time. |
| 4107 | + after: Union[:class:`abc.Snowflake`, :class:`datetime.datetime`] |
| 4108 | + Retrieve join requests after this date or join request. |
| 4109 | + If a datetime is provided, it is recommended to use a UTC aware datetime. |
| 4110 | + If the datetime is naive, it is assumed to be local time. |
| 4111 | + oldest_first: :class:`bool` |
| 4112 | + If set to ``True``, return join requests in oldest->newest order. |
| 4113 | + Defaults to ``True`` if ``after`` is specified, otherwise ``False``. |
| 4114 | +
|
| 4115 | + Raises |
| 4116 | + ------- |
| 4117 | + ValueError |
| 4118 | + :attr:`JoinRequestStatus.started` was passed as the ``status``. |
| 4119 | + Forbidden |
| 4120 | + You do not have permissions to fetch the join requests. |
| 4121 | + HTTPException |
| 4122 | + Fetching the join requests failed. |
| 4123 | +
|
| 4124 | + Yields |
| 4125 | + ------- |
| 4126 | + :class:`JoinRequest` |
| 4127 | + The join request that was fetched. |
| 4128 | + """ |
| 4129 | + if status is JoinRequestStatus.started: |
| 4130 | + raise ValueError('Join requests with a status of JoinRequestStatus.started cannot be queried') |
| 4131 | + |
| 4132 | + if isinstance(before, datetime.datetime): |
| 4133 | + before = Object(id=utils.time_snowflake(before, high=False)) |
| 4134 | + if isinstance(after, datetime.datetime): |
| 4135 | + after = Object(id=utils.time_snowflake(after, high=True)) |
| 4136 | + |
| 4137 | + state = self._state |
| 4138 | + endpoint = state.http.get_join_requests |
| 4139 | + |
| 4140 | + def _cursor(request: JoinRequestPayload, *, high: bool) -> int: |
| 4141 | + if status is JoinRequestStatus.submitted: |
| 4142 | + return int(request['id']) |
| 4143 | + # actioned_at exists as a snowflake but it's deprecated |
| 4144 | + return utils.time_snowflake( |
| 4145 | + utils.parse_time(request['reviewed_at']), # pyright: ignore[reportTypedDictNotRequiredAccess] |
| 4146 | + high=high, |
| 4147 | + ) |
| 4148 | + |
| 4149 | + async def _before_strategy(retrieve: int, before: Optional[Snowflake], limit: Optional[int]): |
| 4150 | + before_id = before.id if before else None |
| 4151 | + data = (await endpoint(self.id, status.value, limit=retrieve, before=before_id))['guild_join_requests'] |
| 4152 | + |
| 4153 | + if data: |
| 4154 | + if limit is not None: |
| 4155 | + limit -= len(data) |
| 4156 | + |
| 4157 | + before = Object(id=_cursor(data[-1], high=True)) |
| 4158 | + |
| 4159 | + return data, before, limit |
| 4160 | + |
| 4161 | + async def _after_strategy(retrieve: int, after: Optional[Snowflake], limit: Optional[int]): |
| 4162 | + after_id = after.id if after else None |
| 4163 | + data = (await endpoint(self.id, status.value, limit=retrieve, after=after_id))['guild_join_requests'] |
| 4164 | + |
| 4165 | + if data: |
| 4166 | + if limit is not None: |
| 4167 | + limit -= len(data) |
| 4168 | + |
| 4169 | + after = Object(id=_cursor(data[-1], high=False)) |
| 4170 | + |
| 4171 | + return data, after, limit |
| 4172 | + |
| 4173 | + if oldest_first is MISSING: |
| 4174 | + reverse = after is not MISSING |
| 4175 | + else: |
| 4176 | + reverse = oldest_first |
| 4177 | + |
| 4178 | + predicate = None |
| 4179 | + |
| 4180 | + if reverse: |
| 4181 | + strategy, state_ = _after_strategy, after or OLDEST_OBJECT |
| 4182 | + if before is not MISSING: |
| 4183 | + predicate = lambda r: _cursor(r, high=False) < before.id |
| 4184 | + else: |
| 4185 | + strategy, state_ = _before_strategy, before |
| 4186 | + if after is not MISSING: |
| 4187 | + predicate = lambda r: _cursor(r, high=True) > after.id |
| 4188 | + |
| 4189 | + seen: Set[int] = set() |
| 4190 | + |
| 4191 | + while True: |
| 4192 | + retrieve = 100 if limit is None else min(limit, 100) |
| 4193 | + if retrieve < 1: |
| 4194 | + return |
| 4195 | + |
| 4196 | + data, state_, limit = await strategy(retrieve, state_, limit) |
| 4197 | + |
| 4198 | + # Terminate loop on next iteration; there's no data left after this |
| 4199 | + if len(data) < 100: |
| 4200 | + limit = 0 |
| 4201 | + |
| 4202 | + if data: |
| 4203 | + # Everything actioned in the same millisecond (somewhat common) |
| 4204 | + # as the page boundary is returned again on the next page |
| 4205 | + boundary = _cursor(data[-1], high=False) |
| 4206 | + fresh = [request for request in data if int(request['id']) not in seen] |
| 4207 | + seen = {int(request['id']) for request in data if _cursor(request, high=False) == boundary} |
| 4208 | + data = fresh |
| 4209 | + |
| 4210 | + if predicate: |
| 4211 | + data = filter(predicate, data) |
| 4212 | + |
| 4213 | + for raw_request in data: |
| 4214 | + yield JoinRequest(data=raw_request, state=state) |
| 4215 | + |
3967 | 4216 | async def kick(self, user: Snowflake, *, reason: Optional[str] = None) -> None: |
3968 | 4217 | """|coro| |
3969 | 4218 |
|
|
0 commit comments