diff --git a/getstream/chat/async_channel.py b/getstream/chat/async_channel.py index 15089ab3..a206ccb6 100644 --- a/getstream/chat/async_channel.py +++ b/getstream/chat/async_channel.py @@ -131,6 +131,33 @@ async def update( self._sync_from_response(response.data) return response + @attach_channel_cid_async + async def query_channel_bans( + self, + exclude_expired_bans: Optional[bool] = None, + include_total: Optional[bool] = None, + limit: Optional[int] = None, + next: Optional[str] = None, + prev: Optional[str] = None, + user_id: Optional[str] = None, + filter_conditions: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryChannelBansResponse]: + response = await self.client.query_channel_bans( + type=self.channel_type, + id=self.channel_id, + exclude_expired_bans=exclude_expired_bans, + include_total=include_total, + limit=limit, + next=next, + prev=prev, + user_id=user_id, + filter_conditions=filter_conditions, + user=user, + ) + self._sync_from_response(response.data) + return response + @attach_channel_cid_async async def delete_draft( self, parent_id: Optional[str] = None, user_id: Optional[str] = None @@ -255,6 +282,7 @@ async def send_message( include_mentioned_members: Optional[bool] = None, keep_channel_hidden: Optional[bool] = None, pending: Optional[bool] = None, + scheduled_at: Optional[datetime] = None, skip_enrich_url: Optional[bool] = None, skip_push: Optional[bool] = None, pending_message_metadata: Optional[Dict[str, str]] = None, @@ -268,6 +296,7 @@ async def send_message( include_mentioned_members=include_mentioned_members, keep_channel_hidden=keep_channel_hidden, pending=pending, + scheduled_at=scheduled_at, skip_enrich_url=skip_enrich_url, skip_push=skip_push, pending_message_metadata=pending_message_metadata, diff --git a/getstream/chat/async_rest_client.py b/getstream/chat/async_rest_client.py index 5f89a2ba..49e30af3 100644 --- a/getstream/chat/async_rest_client.py +++ b/getstream/chat/async_rest_client.py @@ -110,7 +110,7 @@ async def get_campaign( next: Optional[str] = None, limit: Optional[int] = None, ) -> StreamResponse[GetCampaignResponse]: - query_params = build_query_param(prev=prev, next=next, limit=limit) + query_params = build_query_param(**{"prev": prev, "next": next, "limit": limit}) path_params = { "id": id, } @@ -244,6 +244,7 @@ async def channel_batch_update( operation: str, filter: Dict[str, object], hide_history_before: Optional[datetime] = None, + synchronous: Optional[bool] = None, custom_unset: Optional[List[str]] = None, members: Optional[List[ChannelBatchMemberRequest]] = None, custom_set: Optional[Dict[str, object]] = None, @@ -253,6 +254,7 @@ async def channel_batch_update( operation=operation, filter=filter, hide_history_before=hide_history_before, + synchronous=synchronous, custom_unset=custom_unset, members=members, custom_set=custom_set, @@ -282,7 +284,7 @@ async def mark_delivered( user_id: Optional[str] = None, latest_delivered_messages: Optional[List[DeliveredMessagePayload]] = None, ) -> StreamResponse[MarkDeliveredResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = MarkDeliveredRequest( latest_delivered_messages=latest_delivered_messages ).to_dict() @@ -298,11 +300,12 @@ async def grouped_query_channels( self, limit: Optional[int] = None, user_id: Optional[str] = None, + sort: Optional[List[SortParamRequest]] = None, groups: Optional[Dict[str, GroupedChannelsGroupRequest]] = None, user: Optional[UserRequest] = None, ) -> StreamResponse[GroupedQueryChannelsResponse]: json = GroupedQueryChannelsRequest( - limit=limit, user_id=user_id, groups=groups, user=user + limit=limit, user_id=user_id, sort=sort, groups=groups, user=user ).to_dict() return await self.post( "/api/v2/chat/channels/grouped", GroupedQueryChannelsResponse, json=json @@ -364,7 +367,7 @@ async def delete_channel( skip_truncate: Optional[bool] = None, ) -> StreamResponse[DeleteChannelResponse]: query_params = build_query_param( - hard_delete=hard_delete, skip_truncate=skip_truncate + **{"hard_delete": hard_delete, "skip_truncate": skip_truncate} ) path_params = { "type": type, @@ -394,16 +397,18 @@ async def get_channel( user_id: Optional[str] = None, ) -> StreamResponse[ChannelStateResponse]: query_params = build_query_param( - state=state, - messages_limit=messages_limit, - members_limit=members_limit, - watchers_limit=watchers_limit, - messages_id_lt=messages_id_lt, - messages_id_lte=messages_id_lte, - messages_id_gt=messages_id_gt, - messages_id_gte=messages_id_gte, - messages_id_around=messages_id_around, - user_id=user_id, + **{ + "state": state, + "messages_limit": messages_limit, + "members_limit": members_limit, + "watchers_limit": watchers_limit, + "messages_id_lt": messages_id_lt, + "messages_id_lte": messages_id_lte, + "messages_id_gt": messages_id_gt, + "messages_id_gte": messages_id_gte, + "messages_id_around": messages_id_around, + "user_id": user_id, + } ) path_params = { "type": type, @@ -495,6 +500,41 @@ async def update_channel( json=json, ) + @telemetry.operation_name("getstream.api.chat.query_channel_bans") + async def query_channel_bans( + self, + type: str, + id: str, + exclude_expired_bans: Optional[bool] = None, + include_total: Optional[bool] = None, + limit: Optional[int] = None, + next: Optional[str] = None, + prev: Optional[str] = None, + user_id: Optional[str] = None, + filter_conditions: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryChannelBansResponse]: + path_params = { + "type": type, + "id": id, + } + json = QueryChannelBansRequest( + exclude_expired_bans=exclude_expired_bans, + include_total=include_total, + limit=limit, + next=next, + prev=prev, + user_id=user_id, + filter_conditions=filter_conditions, + user=user, + ).to_dict() + return await self.post( + "/api/v2/chat/channels/{type}/{id}/bans/query", + QueryChannelBansResponse, + path_params=path_params, + json=json, + ) + @telemetry.operation_name("getstream.api.chat.delete_draft") async def delete_draft( self, @@ -503,7 +543,7 @@ async def delete_draft( parent_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[Response]: - query_params = build_query_param(parent_id=parent_id, user_id=user_id) + query_params = build_query_param(**{"parent_id": parent_id, "user_id": user_id}) path_params = { "type": type, "id": id, @@ -523,7 +563,7 @@ async def get_draft( parent_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[GetDraftResponse]: - query_params = build_query_param(parent_id=parent_id, user_id=user_id) + query_params = build_query_param(**{"parent_id": parent_id, "user_id": user_id}) path_params = { "type": type, "id": id, @@ -555,7 +595,7 @@ async def send_event( async def delete_channel_file( self, type: str, id: str, url: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) path_params = { "type": type, "id": id, @@ -580,7 +620,7 @@ async def upload_channel_file( "id": id, } json = UploadChannelFileRequest(file=file, user=user).to_dict() - return await self.post( + return await self._upload_multipart( "/api/v2/chat/channels/{type}/{id}/file", UploadChannelFileResponse, path_params=path_params, @@ -614,7 +654,7 @@ async def hide_channel( async def delete_channel_image( self, type: str, id: str, url: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) path_params = { "type": type, "id": id, @@ -642,7 +682,7 @@ async def upload_channel_image( json = UploadChannelRequest( file=file, upload_sizes=upload_sizes, user=user ).to_dict() - return await self.post( + return await self._upload_multipart( "/api/v2/chat/channels/{type}/{id}/image", UploadChannelResponse, path_params=path_params, @@ -658,7 +698,7 @@ async def update_member_partial( unset: Optional[List[str]] = None, set: Optional[Dict[str, object]] = None, ) -> StreamResponse[UpdateMemberPartialResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "type": type, "id": id, @@ -683,6 +723,7 @@ async def send_message( include_mentioned_members: Optional[bool] = None, keep_channel_hidden: Optional[bool] = None, pending: Optional[bool] = None, + scheduled_at: Optional[datetime] = None, skip_enrich_url: Optional[bool] = None, skip_push: Optional[bool] = None, pending_message_metadata: Optional[Dict[str, str]] = None, @@ -698,6 +739,7 @@ async def send_message( include_mentioned_members=include_mentioned_members, keep_channel_hidden=keep_channel_hidden, pending=pending, + scheduled_at=scheduled_at, skip_enrich_url=skip_enrich_url, skip_push=skip_push, pending_message_metadata=pending_message_metadata, @@ -718,7 +760,7 @@ async def get_many_messages( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetManyMessagesResponse]: query_params = build_query_param( - ids=ids, member_custom_include=member_custom_include + **{"ids": ids, "member_custom_include": member_custom_include} ) path_params = { "type": type, @@ -753,21 +795,23 @@ async def get_pinned_messages( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetPinnedMessagesResponse]: query_params = build_query_param( - limit=limit, - offset=offset, - id_gte=id_gte, - id_gt=id_gt, - id_lte=id_lte, - id_lt=id_lt, - pinned_at_after_or_equal=pinned_at_after_or_equal, - pinned_at_after=pinned_at_after, - pinned_at_before_or_equal=pinned_at_before_or_equal, - pinned_at_before=pinned_at_before, - id_around=id_around, - pinned_at_around=pinned_at_around, - user_id=user_id, - sort=sort, - member_custom_include=member_custom_include, + **{ + "limit": limit, + "offset": offset, + "id_gte": id_gte, + "id_gt": id_gt, + "id_lte": id_lte, + "id_lt": id_lt, + "pinned_at_after_or_equal": pinned_at_after_or_equal, + "pinned_at_after": pinned_at_after, + "pinned_at_before_or_equal": pinned_at_before_or_equal, + "pinned_at_before": pinned_at_before, + "id_around": id_around, + "pinned_at_around": pinned_at_around, + "user_id": user_id, + "sort": sort, + "member_custom_include": member_custom_include, + } ) path_params = { "type": type, @@ -1226,7 +1270,7 @@ async def export_channels( async def query_members( self, payload: Optional[QueryMembersPayload] = None ) -> StreamResponse[MembersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/chat/members", MembersResponse, query_params=query_params ) @@ -1256,7 +1300,7 @@ async def delete_message( delete_for_me: Optional[bool] = None, ) -> StreamResponse[DeleteMessageResponse]: query_params = build_query_param( - hard=hard, deleted_by=deleted_by, delete_for_me=delete_for_me + **{"hard": hard, "deleted_by": deleted_by, "delete_for_me": delete_for_me} ) path_params = { "id": id, @@ -1272,7 +1316,9 @@ async def delete_message( async def get_message( self, id: str, show_deleted_message: Optional[bool] = None ) -> StreamResponse[GetMessageResponse]: - query_params = build_query_param(show_deleted_message=show_deleted_message) + query_params = build_query_param( + **{"show_deleted_message": show_deleted_message} + ) path_params = { "id": id, } @@ -1424,7 +1470,7 @@ async def send_reaction( async def delete_reaction( self, id: str, type: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteReactionResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, "type": type, @@ -1440,7 +1486,7 @@ async def delete_reaction( async def get_reactions( self, id: str, limit: Optional[int] = None, offset: Optional[int] = None ) -> StreamResponse[GetReactionsResponse]: - query_params = build_query_param(limit=limit, offset=offset) + query_params = build_query_param(**{"limit": limit, "offset": offset}) path_params = { "id": id, } @@ -1517,6 +1563,8 @@ async def cast_poll_vote( self, message_id: str, poll_id: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, vote: Optional[VoteData] = None, @@ -1525,7 +1573,13 @@ async def cast_poll_vote( "message_id": message_id, "poll_id": poll_id, } - json = CastPollVoteRequest(user_id=user_id, user=user, vote=vote).to_dict() + json = CastPollVoteRequest( + config_key=config_key, + config_team=config_team, + user_id=user_id, + user=user, + vote=vote, + ).to_dict() return await self.post( "/api/v2/chat/messages/{message_id}/polls/{poll_id}/vote", PollVoteResponse, @@ -1537,7 +1591,7 @@ async def cast_poll_vote( async def delete_poll_vote( self, message_id: str, poll_id: str, vote_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollVoteResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "message_id": message_id, "poll_id": poll_id, @@ -1554,7 +1608,7 @@ async def delete_poll_vote( async def delete_reminder( self, message_id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteReminderResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "message_id": message_id, } @@ -1569,6 +1623,7 @@ async def delete_reminder( async def update_reminder( self, message_id: str, + expires_at: Optional[datetime] = None, remind_at: Optional[datetime] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, @@ -1577,7 +1632,7 @@ async def update_reminder( "message_id": message_id, } json = UpdateReminderRequest( - remind_at=remind_at, user_id=user_id, user=user + expires_at=expires_at, remind_at=remind_at, user_id=user_id, user=user ).to_dict() return await self.patch( "/api/v2/chat/messages/{message_id}/reminders", @@ -1590,6 +1645,7 @@ async def update_reminder( async def create_reminder( self, message_id: str, + expires_at: Optional[datetime] = None, remind_at: Optional[datetime] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, @@ -1598,7 +1654,7 @@ async def create_reminder( "message_id": message_id, } json = CreateReminderRequest( - remind_at=remind_at, user_id=user_id, user=user + expires_at=expires_at, remind_at=remind_at, user_id=user_id, user=user ).to_dict() return await self.post( "/api/v2/chat/messages/{message_id}/reminders", @@ -1621,14 +1677,16 @@ async def get_replies( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetRepliesResponse]: query_params = build_query_param( - limit=limit, - id_gte=id_gte, - id_gt=id_gt, - id_lte=id_lte, - id_lt=id_lt, - id_around=id_around, - sort=sort, - member_custom_include=member_custom_include, + **{ + "limit": limit, + "id_gte": id_gte, + "id_gt": id_gt, + "id_lte": id_lte, + "id_lt": id_lt, + "id_around": id_around, + "sort": sort, + "member_custom_include": member_custom_include, + } ) path_params = { "parent_id": parent_id, @@ -1644,7 +1702,7 @@ async def get_replies( async def query_message_flags( self, payload: Optional[QueryMessageFlagsPayload] = None ) -> StreamResponse[QueryMessageFlagsResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/chat/moderation/flags/message", QueryMessageFlagsResponse, @@ -1687,7 +1745,9 @@ async def get_predefined_filters( include_stats: Optional[bool] = None, sort: Optional[List[SortParamRequest]] = None, ) -> StreamResponse[QueryPredefinedFiltersResponse]: - query_params = build_query_param(include_stats=include_stats, sort=sort) + query_params = build_query_param( + **{"include_stats": include_stats, "sort": sort} + ) return await self.get( "/api/v2/chat/predefined_filters", QueryPredefinedFiltersResponse, @@ -1762,7 +1822,7 @@ async def update_predefined_filter( async def query_banned_users( self, payload: Optional[QueryBannedUsersPayload] = None ) -> StreamResponse[QueryBannedUsersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/chat/query_banned_users", QueryBannedUsersResponse, @@ -1773,7 +1833,7 @@ async def query_banned_users( async def query_future_channel_bans( self, payload: Optional[QueryFutureChannelBansPayload] = None ) -> StreamResponse[QueryFutureChannelBansResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/chat/query_future_channel_bans", QueryFutureChannelBansResponse, @@ -1854,11 +1914,79 @@ async def get_retention_policy_runs( json=json, ) + @telemetry.operation_name("getstream.api.chat.query_scheduled_messages") + async def query_scheduled_messages( + self, + limit: Optional[int] = None, + next: Optional[str] = None, + user_id: Optional[str] = None, + sort: Optional[List[SortParamRequest]] = None, + filter: Optional[ScheduledMessagesFilter] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryScheduledMessagesResponse]: + json = QueryScheduledMessagesRequest( + limit=limit, next=next, user_id=user_id, sort=sort, filter=filter, user=user + ).to_dict() + return await self.post( + "/api/v2/chat/scheduled_messages/query", + QueryScheduledMessagesResponse, + json=json, + ) + + @telemetry.operation_name("getstream.api.chat.delete_scheduled_message") + async def delete_scheduled_message( + self, id: str + ) -> StreamResponse[DeleteScheduledMessageResponse]: + path_params = { + "id": id, + } + return await self.delete( + "/api/v2/chat/scheduled_messages/{id}", + DeleteScheduledMessageResponse, + path_params=path_params, + ) + + @telemetry.operation_name("getstream.api.chat.update_scheduled_message") + async def update_scheduled_message( + self, + id: str, + scheduled_at: Optional[datetime] = None, + message: Optional[MessageRequest] = None, + ) -> StreamResponse[UpdateScheduledMessageResponse]: + path_params = { + "id": id, + } + json = UpdateScheduledMessageRequest( + scheduled_at=scheduled_at, message=message + ).to_dict() + return await self.patch( + "/api/v2/chat/scheduled_messages/{id}", + UpdateScheduledMessageResponse, + path_params=path_params, + json=json, + ) + + @telemetry.operation_name("getstream.api.chat.send_scheduled_message_now") + async def send_scheduled_message_now( + self, + id: str, + ) -> StreamResponse[SendScheduledMessageNowResponse]: + path_params = { + "id": id, + } + json = SendScheduledMessageNowRequest().to_dict() + return await self.post( + "/api/v2/chat/scheduled_messages/{id}/send_now", + SendScheduledMessageNowResponse, + path_params=path_params, + json=json, + ) + @telemetry.operation_name("getstream.api.chat.search") async def search( self, payload: Optional[SearchPayload] = None ) -> StreamResponse[SearchResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/chat/search", SearchResponse, query_params=query_params ) @@ -2068,9 +2196,11 @@ async def get_thread( member_limit: Optional[int] = None, ) -> StreamResponse[GetThreadResponse]: query_params = build_query_param( - reply_limit=reply_limit, - participant_limit=participant_limit, - member_limit=member_limit, + **{ + "reply_limit": reply_limit, + "participant_limit": participant_limit, + "member_limit": member_limit, + } ) path_params = { "message_id": message_id, @@ -2108,7 +2238,7 @@ async def update_thread_partial( async def unread_counts( self, user_id: Optional[str] = None ) -> StreamResponse[WrappedUnreadCountsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return await self.get( "/api/v2/chat/unread", WrappedUnreadCountsResponse, diff --git a/getstream/chat/channel.py b/getstream/chat/channel.py index da454a14..4a453f34 100644 --- a/getstream/chat/channel.py +++ b/getstream/chat/channel.py @@ -131,6 +131,33 @@ def update( self._sync_from_response(response.data) return response + @attach_channel_cid + def query_channel_bans( + self, + exclude_expired_bans: Optional[bool] = None, + include_total: Optional[bool] = None, + limit: Optional[int] = None, + next: Optional[str] = None, + prev: Optional[str] = None, + user_id: Optional[str] = None, + filter_conditions: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryChannelBansResponse]: + response = self.client.query_channel_bans( + type=self.channel_type, + id=self.channel_id, + exclude_expired_bans=exclude_expired_bans, + include_total=include_total, + limit=limit, + next=next, + prev=prev, + user_id=user_id, + filter_conditions=filter_conditions, + user=user, + ) + self._sync_from_response(response.data) + return response + @attach_channel_cid def delete_draft( self, parent_id: Optional[str] = None, user_id: Optional[str] = None @@ -255,6 +282,7 @@ def send_message( include_mentioned_members: Optional[bool] = None, keep_channel_hidden: Optional[bool] = None, pending: Optional[bool] = None, + scheduled_at: Optional[datetime] = None, skip_enrich_url: Optional[bool] = None, skip_push: Optional[bool] = None, pending_message_metadata: Optional[Dict[str, str]] = None, @@ -268,6 +296,7 @@ def send_message( include_mentioned_members=include_mentioned_members, keep_channel_hidden=keep_channel_hidden, pending=pending, + scheduled_at=scheduled_at, skip_enrich_url=skip_enrich_url, skip_push=skip_push, pending_message_metadata=pending_message_metadata, diff --git a/getstream/chat/rest_client.py b/getstream/chat/rest_client.py index 9c7d6ce0..4327df52 100644 --- a/getstream/chat/rest_client.py +++ b/getstream/chat/rest_client.py @@ -108,7 +108,7 @@ def get_campaign( next: Optional[str] = None, limit: Optional[int] = None, ) -> StreamResponse[GetCampaignResponse]: - query_params = build_query_param(prev=prev, next=next, limit=limit) + query_params = build_query_param(**{"prev": prev, "next": next, "limit": limit}) path_params = { "id": id, } @@ -240,6 +240,7 @@ def channel_batch_update( operation: str, filter: Dict[str, object], hide_history_before: Optional[datetime] = None, + synchronous: Optional[bool] = None, custom_unset: Optional[List[str]] = None, members: Optional[List[ChannelBatchMemberRequest]] = None, custom_set: Optional[Dict[str, object]] = None, @@ -249,6 +250,7 @@ def channel_batch_update( operation=operation, filter=filter, hide_history_before=hide_history_before, + synchronous=synchronous, custom_unset=custom_unset, members=members, custom_set=custom_set, @@ -278,7 +280,7 @@ def mark_delivered( user_id: Optional[str] = None, latest_delivered_messages: Optional[List[DeliveredMessagePayload]] = None, ) -> StreamResponse[MarkDeliveredResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = MarkDeliveredRequest( latest_delivered_messages=latest_delivered_messages ).to_dict() @@ -294,11 +296,12 @@ def grouped_query_channels( self, limit: Optional[int] = None, user_id: Optional[str] = None, + sort: Optional[List[SortParamRequest]] = None, groups: Optional[Dict[str, GroupedChannelsGroupRequest]] = None, user: Optional[UserRequest] = None, ) -> StreamResponse[GroupedQueryChannelsResponse]: json = GroupedQueryChannelsRequest( - limit=limit, user_id=user_id, groups=groups, user=user + limit=limit, user_id=user_id, sort=sort, groups=groups, user=user ).to_dict() return self.post( "/api/v2/chat/channels/grouped", GroupedQueryChannelsResponse, json=json @@ -358,7 +361,7 @@ def delete_channel( skip_truncate: Optional[bool] = None, ) -> StreamResponse[DeleteChannelResponse]: query_params = build_query_param( - hard_delete=hard_delete, skip_truncate=skip_truncate + **{"hard_delete": hard_delete, "skip_truncate": skip_truncate} ) path_params = { "type": type, @@ -388,16 +391,18 @@ def get_channel( user_id: Optional[str] = None, ) -> StreamResponse[ChannelStateResponse]: query_params = build_query_param( - state=state, - messages_limit=messages_limit, - members_limit=members_limit, - watchers_limit=watchers_limit, - messages_id_lt=messages_id_lt, - messages_id_lte=messages_id_lte, - messages_id_gt=messages_id_gt, - messages_id_gte=messages_id_gte, - messages_id_around=messages_id_around, - user_id=user_id, + **{ + "state": state, + "messages_limit": messages_limit, + "members_limit": members_limit, + "watchers_limit": watchers_limit, + "messages_id_lt": messages_id_lt, + "messages_id_lte": messages_id_lte, + "messages_id_gt": messages_id_gt, + "messages_id_gte": messages_id_gte, + "messages_id_around": messages_id_around, + "user_id": user_id, + } ) path_params = { "type": type, @@ -489,6 +494,41 @@ def update_channel( json=json, ) + @telemetry.operation_name("getstream.api.chat.query_channel_bans") + def query_channel_bans( + self, + type: str, + id: str, + exclude_expired_bans: Optional[bool] = None, + include_total: Optional[bool] = None, + limit: Optional[int] = None, + next: Optional[str] = None, + prev: Optional[str] = None, + user_id: Optional[str] = None, + filter_conditions: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryChannelBansResponse]: + path_params = { + "type": type, + "id": id, + } + json = QueryChannelBansRequest( + exclude_expired_bans=exclude_expired_bans, + include_total=include_total, + limit=limit, + next=next, + prev=prev, + user_id=user_id, + filter_conditions=filter_conditions, + user=user, + ).to_dict() + return self.post( + "/api/v2/chat/channels/{type}/{id}/bans/query", + QueryChannelBansResponse, + path_params=path_params, + json=json, + ) + @telemetry.operation_name("getstream.api.chat.delete_draft") def delete_draft( self, @@ -497,7 +537,7 @@ def delete_draft( parent_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[Response]: - query_params = build_query_param(parent_id=parent_id, user_id=user_id) + query_params = build_query_param(**{"parent_id": parent_id, "user_id": user_id}) path_params = { "type": type, "id": id, @@ -517,7 +557,7 @@ def get_draft( parent_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[GetDraftResponse]: - query_params = build_query_param(parent_id=parent_id, user_id=user_id) + query_params = build_query_param(**{"parent_id": parent_id, "user_id": user_id}) path_params = { "type": type, "id": id, @@ -549,7 +589,7 @@ def send_event( def delete_channel_file( self, type: str, id: str, url: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) path_params = { "type": type, "id": id, @@ -574,7 +614,7 @@ def upload_channel_file( "id": id, } json = UploadChannelFileRequest(file=file, user=user).to_dict() - return self.post( + return self._upload_multipart( "/api/v2/chat/channels/{type}/{id}/file", UploadChannelFileResponse, path_params=path_params, @@ -608,7 +648,7 @@ def hide_channel( def delete_channel_image( self, type: str, id: str, url: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) path_params = { "type": type, "id": id, @@ -636,7 +676,7 @@ def upload_channel_image( json = UploadChannelRequest( file=file, upload_sizes=upload_sizes, user=user ).to_dict() - return self.post( + return self._upload_multipart( "/api/v2/chat/channels/{type}/{id}/image", UploadChannelResponse, path_params=path_params, @@ -652,7 +692,7 @@ def update_member_partial( unset: Optional[List[str]] = None, set: Optional[Dict[str, object]] = None, ) -> StreamResponse[UpdateMemberPartialResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "type": type, "id": id, @@ -677,6 +717,7 @@ def send_message( include_mentioned_members: Optional[bool] = None, keep_channel_hidden: Optional[bool] = None, pending: Optional[bool] = None, + scheduled_at: Optional[datetime] = None, skip_enrich_url: Optional[bool] = None, skip_push: Optional[bool] = None, pending_message_metadata: Optional[Dict[str, str]] = None, @@ -692,6 +733,7 @@ def send_message( include_mentioned_members=include_mentioned_members, keep_channel_hidden=keep_channel_hidden, pending=pending, + scheduled_at=scheduled_at, skip_enrich_url=skip_enrich_url, skip_push=skip_push, pending_message_metadata=pending_message_metadata, @@ -712,7 +754,7 @@ def get_many_messages( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetManyMessagesResponse]: query_params = build_query_param( - ids=ids, member_custom_include=member_custom_include + **{"ids": ids, "member_custom_include": member_custom_include} ) path_params = { "type": type, @@ -747,21 +789,23 @@ def get_pinned_messages( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetPinnedMessagesResponse]: query_params = build_query_param( - limit=limit, - offset=offset, - id_gte=id_gte, - id_gt=id_gt, - id_lte=id_lte, - id_lt=id_lt, - pinned_at_after_or_equal=pinned_at_after_or_equal, - pinned_at_after=pinned_at_after, - pinned_at_before_or_equal=pinned_at_before_or_equal, - pinned_at_before=pinned_at_before, - id_around=id_around, - pinned_at_around=pinned_at_around, - user_id=user_id, - sort=sort, - member_custom_include=member_custom_include, + **{ + "limit": limit, + "offset": offset, + "id_gte": id_gte, + "id_gt": id_gt, + "id_lte": id_lte, + "id_lt": id_lt, + "pinned_at_after_or_equal": pinned_at_after_or_equal, + "pinned_at_after": pinned_at_after, + "pinned_at_before_or_equal": pinned_at_before_or_equal, + "pinned_at_before": pinned_at_before, + "id_around": id_around, + "pinned_at_around": pinned_at_around, + "user_id": user_id, + "sort": sort, + "member_custom_include": member_custom_include, + } ) path_params = { "type": type, @@ -1214,7 +1258,7 @@ def export_channels( def query_members( self, payload: Optional[QueryMembersPayload] = None ) -> StreamResponse[MembersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get( "/api/v2/chat/members", MembersResponse, query_params=query_params ) @@ -1244,7 +1288,7 @@ def delete_message( delete_for_me: Optional[bool] = None, ) -> StreamResponse[DeleteMessageResponse]: query_params = build_query_param( - hard=hard, deleted_by=deleted_by, delete_for_me=delete_for_me + **{"hard": hard, "deleted_by": deleted_by, "delete_for_me": delete_for_me} ) path_params = { "id": id, @@ -1260,7 +1304,9 @@ def delete_message( def get_message( self, id: str, show_deleted_message: Optional[bool] = None ) -> StreamResponse[GetMessageResponse]: - query_params = build_query_param(show_deleted_message=show_deleted_message) + query_params = build_query_param( + **{"show_deleted_message": show_deleted_message} + ) path_params = { "id": id, } @@ -1412,7 +1458,7 @@ def send_reaction( def delete_reaction( self, id: str, type: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteReactionResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, "type": type, @@ -1428,7 +1474,7 @@ def delete_reaction( def get_reactions( self, id: str, limit: Optional[int] = None, offset: Optional[int] = None ) -> StreamResponse[GetReactionsResponse]: - query_params = build_query_param(limit=limit, offset=offset) + query_params = build_query_param(**{"limit": limit, "offset": offset}) path_params = { "id": id, } @@ -1505,6 +1551,8 @@ def cast_poll_vote( self, message_id: str, poll_id: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, vote: Optional[VoteData] = None, @@ -1513,7 +1561,13 @@ def cast_poll_vote( "message_id": message_id, "poll_id": poll_id, } - json = CastPollVoteRequest(user_id=user_id, user=user, vote=vote).to_dict() + json = CastPollVoteRequest( + config_key=config_key, + config_team=config_team, + user_id=user_id, + user=user, + vote=vote, + ).to_dict() return self.post( "/api/v2/chat/messages/{message_id}/polls/{poll_id}/vote", PollVoteResponse, @@ -1525,7 +1579,7 @@ def cast_poll_vote( def delete_poll_vote( self, message_id: str, poll_id: str, vote_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollVoteResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "message_id": message_id, "poll_id": poll_id, @@ -1542,7 +1596,7 @@ def delete_poll_vote( def delete_reminder( self, message_id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteReminderResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "message_id": message_id, } @@ -1557,6 +1611,7 @@ def delete_reminder( def update_reminder( self, message_id: str, + expires_at: Optional[datetime] = None, remind_at: Optional[datetime] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, @@ -1565,7 +1620,7 @@ def update_reminder( "message_id": message_id, } json = UpdateReminderRequest( - remind_at=remind_at, user_id=user_id, user=user + expires_at=expires_at, remind_at=remind_at, user_id=user_id, user=user ).to_dict() return self.patch( "/api/v2/chat/messages/{message_id}/reminders", @@ -1578,6 +1633,7 @@ def update_reminder( def create_reminder( self, message_id: str, + expires_at: Optional[datetime] = None, remind_at: Optional[datetime] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, @@ -1586,7 +1642,7 @@ def create_reminder( "message_id": message_id, } json = CreateReminderRequest( - remind_at=remind_at, user_id=user_id, user=user + expires_at=expires_at, remind_at=remind_at, user_id=user_id, user=user ).to_dict() return self.post( "/api/v2/chat/messages/{message_id}/reminders", @@ -1609,14 +1665,16 @@ def get_replies( member_custom_include: Optional[List[str]] = None, ) -> StreamResponse[GetRepliesResponse]: query_params = build_query_param( - limit=limit, - id_gte=id_gte, - id_gt=id_gt, - id_lte=id_lte, - id_lt=id_lt, - id_around=id_around, - sort=sort, - member_custom_include=member_custom_include, + **{ + "limit": limit, + "id_gte": id_gte, + "id_gt": id_gt, + "id_lte": id_lte, + "id_lt": id_lt, + "id_around": id_around, + "sort": sort, + "member_custom_include": member_custom_include, + } ) path_params = { "parent_id": parent_id, @@ -1632,7 +1690,7 @@ def get_replies( def query_message_flags( self, payload: Optional[QueryMessageFlagsPayload] = None ) -> StreamResponse[QueryMessageFlagsResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get( "/api/v2/chat/moderation/flags/message", QueryMessageFlagsResponse, @@ -1675,7 +1733,9 @@ def get_predefined_filters( include_stats: Optional[bool] = None, sort: Optional[List[SortParamRequest]] = None, ) -> StreamResponse[QueryPredefinedFiltersResponse]: - query_params = build_query_param(include_stats=include_stats, sort=sort) + query_params = build_query_param( + **{"include_stats": include_stats, "sort": sort} + ) return self.get( "/api/v2/chat/predefined_filters", QueryPredefinedFiltersResponse, @@ -1750,7 +1810,7 @@ def update_predefined_filter( def query_banned_users( self, payload: Optional[QueryBannedUsersPayload] = None ) -> StreamResponse[QueryBannedUsersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get( "/api/v2/chat/query_banned_users", QueryBannedUsersResponse, @@ -1761,7 +1821,7 @@ def query_banned_users( def query_future_channel_bans( self, payload: Optional[QueryFutureChannelBansPayload] = None ) -> StreamResponse[QueryFutureChannelBansResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get( "/api/v2/chat/query_future_channel_bans", QueryFutureChannelBansResponse, @@ -1840,11 +1900,79 @@ def get_retention_policy_runs( json=json, ) + @telemetry.operation_name("getstream.api.chat.query_scheduled_messages") + def query_scheduled_messages( + self, + limit: Optional[int] = None, + next: Optional[str] = None, + user_id: Optional[str] = None, + sort: Optional[List[SortParamRequest]] = None, + filter: Optional[ScheduledMessagesFilter] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[QueryScheduledMessagesResponse]: + json = QueryScheduledMessagesRequest( + limit=limit, next=next, user_id=user_id, sort=sort, filter=filter, user=user + ).to_dict() + return self.post( + "/api/v2/chat/scheduled_messages/query", + QueryScheduledMessagesResponse, + json=json, + ) + + @telemetry.operation_name("getstream.api.chat.delete_scheduled_message") + def delete_scheduled_message( + self, id: str + ) -> StreamResponse[DeleteScheduledMessageResponse]: + path_params = { + "id": id, + } + return self.delete( + "/api/v2/chat/scheduled_messages/{id}", + DeleteScheduledMessageResponse, + path_params=path_params, + ) + + @telemetry.operation_name("getstream.api.chat.update_scheduled_message") + def update_scheduled_message( + self, + id: str, + scheduled_at: Optional[datetime] = None, + message: Optional[MessageRequest] = None, + ) -> StreamResponse[UpdateScheduledMessageResponse]: + path_params = { + "id": id, + } + json = UpdateScheduledMessageRequest( + scheduled_at=scheduled_at, message=message + ).to_dict() + return self.patch( + "/api/v2/chat/scheduled_messages/{id}", + UpdateScheduledMessageResponse, + path_params=path_params, + json=json, + ) + + @telemetry.operation_name("getstream.api.chat.send_scheduled_message_now") + def send_scheduled_message_now( + self, + id: str, + ) -> StreamResponse[SendScheduledMessageNowResponse]: + path_params = { + "id": id, + } + json = SendScheduledMessageNowRequest().to_dict() + return self.post( + "/api/v2/chat/scheduled_messages/{id}/send_now", + SendScheduledMessageNowResponse, + path_params=path_params, + json=json, + ) + @telemetry.operation_name("getstream.api.chat.search") def search( self, payload: Optional[SearchPayload] = None ) -> StreamResponse[SearchResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get( "/api/v2/chat/search", SearchResponse, query_params=query_params ) @@ -2052,9 +2180,11 @@ def get_thread( member_limit: Optional[int] = None, ) -> StreamResponse[GetThreadResponse]: query_params = build_query_param( - reply_limit=reply_limit, - participant_limit=participant_limit, - member_limit=member_limit, + **{ + "reply_limit": reply_limit, + "participant_limit": participant_limit, + "member_limit": member_limit, + } ) path_params = { "message_id": message_id, @@ -2092,7 +2222,7 @@ def update_thread_partial( def unread_counts( self, user_id: Optional[str] = None ) -> StreamResponse[WrappedUnreadCountsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return self.get( "/api/v2/chat/unread", WrappedUnreadCountsResponse, diff --git a/getstream/common/async_rest_client.py b/getstream/common/async_rest_client.py index 20967244..9170e5f1 100644 --- a/getstream/common/async_rest_client.py +++ b/getstream/common/async_rest_client.py @@ -172,7 +172,9 @@ async def list_block_lists( cursor: Optional[str] = None, limit: Optional[int] = None, ) -> StreamResponse[ListBlockListResponse]: - query_params = build_query_param(team=team, cursor=cursor, limit=limit) + query_params = build_query_param( + **{"team": team, "cursor": cursor, "limit": limit} + ) return await self.get( "/api/v2/blocklists", ListBlockListResponse, query_params=query_params ) @@ -224,7 +226,7 @@ async def import_block_list( async def delete_block_list( self, name: str, team: Optional[str] = None, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(team=team, user_id=user_id) + query_params = build_query_param(**{"team": team, "user_id": user_id}) path_params = { "name": name, } @@ -239,7 +241,7 @@ async def delete_block_list( async def get_block_list( self, name: str, team: Optional[str] = None ) -> StreamResponse[GetBlockListResponse]: - query_params = build_query_param(team=team) + query_params = build_query_param(**{"team": team}) path_params = { "name": name, } @@ -283,6 +285,22 @@ async def update_block_list( json=json, ) + @telemetry.operation_name("getstream.api.common.check_gcp_pub_sub") + async def check_gcp_pub_sub( + self, + gcp_pubsub_event_based_ordering_key_enabled: Optional[bool] = None, + gcp_pubsub_region: Optional[str] = None, + gcp_pubsub_topic: Optional[str] = None, + ) -> StreamResponse[CheckGCPPubSubResponse]: + json = CheckGCPPubSubRequest( + gcp_pubsub_event_based_ordering_key_enabled=gcp_pubsub_event_based_ordering_key_enabled, + gcp_pubsub_region=gcp_pubsub_region, + gcp_pubsub_topic=gcp_pubsub_topic, + ).to_dict() + return await self.post( + "/api/v2/check_gcp_pubsub", CheckGCPPubSubResponse, json=json + ) + @telemetry.operation_name("getstream.api.common.check_push") async def check_push( self, @@ -339,14 +357,14 @@ async def check_sqs( async def delete_device( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(id=id, user_id=user_id) + query_params = build_query_param(**{"id": id, "user_id": user_id}) return await self.delete("/api/v2/devices", Response, query_params=query_params) @telemetry.operation_name("getstream.api.common.list_devices") async def list_devices( self, user_id: Optional[str] = None ) -> StreamResponse[ListDevicesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return await self.get( "/api/v2/devices", ListDevicesResponse, query_params=query_params ) @@ -498,7 +516,7 @@ async def create_import( async def list_import_v2_tasks( self, state: Optional[int] = None ) -> StreamResponse[ListImportV2TasksResponse]: - query_params = build_query_param(state=state) + query_params = build_query_param(**{"state": state}) return await self.get( "/api/v2/imports/v2", ListImportV2TasksResponse, query_params=query_params ) @@ -605,7 +623,7 @@ async def get_import(self, id: str) -> StreamResponse[GetImportResponse]: @telemetry.operation_name("getstream.api.common.get_og") async def get_og(self, url: str) -> StreamResponse[GetOGResponse]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return await self.get("/api/v2/og", GetOGResponse, query_params=query_params) @telemetry.operation_name("getstream.api.common.list_permissions") @@ -688,11 +706,14 @@ async def create_poll( name: str, allow_answers: Optional[bool] = None, allow_user_suggested_options: Optional[bool] = None, + config_key: Optional[str] = None, + config_team: Optional[str] = None, description: Optional[str] = None, enforce_unique_vote: Optional[bool] = None, id: Optional[str] = None, is_closed: Optional[bool] = None, max_votes_allowed: Optional[int] = None, + team: Optional[str] = None, user_id: Optional[str] = None, voting_visibility: Optional[str] = None, options: Optional[List[PollOptionInput]] = None, @@ -703,11 +724,14 @@ async def create_poll( name=name, allow_answers=allow_answers, allow_user_suggested_options=allow_user_suggested_options, + config_key=config_key, + config_team=config_team, description=description, enforce_unique_vote=enforce_unique_vote, id=id, is_closed=is_closed, max_votes_allowed=max_votes_allowed, + team=team, user_id=user_id, voting_visibility=voting_visibility, options=options, @@ -723,6 +747,8 @@ async def update_poll( name: str, allow_answers: Optional[bool] = None, allow_user_suggested_options: Optional[bool] = None, + config_key: Optional[str] = None, + config_team: Optional[str] = None, description: Optional[str] = None, enforce_unique_vote: Optional[bool] = None, is_closed: Optional[bool] = None, @@ -738,6 +764,8 @@ async def update_poll( name=name, allow_answers=allow_answers, allow_user_suggested_options=allow_user_suggested_options, + config_key=config_key, + config_team=config_team, description=description, enforce_unique_vote=enforce_unique_vote, is_closed=is_closed, @@ -760,7 +788,7 @@ async def query_polls( sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryPollsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = QueryPollsRequest( limit=limit, next=next, prev=prev, sort=sort, filter=filter ).to_dict() @@ -775,7 +803,7 @@ async def query_polls( async def delete_poll( self, poll_id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -790,7 +818,7 @@ async def delete_poll( async def get_poll( self, poll_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -805,6 +833,8 @@ async def get_poll( async def update_poll_partial( self, poll_id: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, unset: Optional[List[str]] = None, set: Optional[Dict[str, object]] = None, @@ -814,7 +844,12 @@ async def update_poll_partial( "poll_id": poll_id, } json = UpdatePollPartialRequest( - user_id=user_id, unset=unset, set=set, user=user + config_key=config_key, + config_team=config_team, + user_id=user_id, + unset=unset, + set=set, + user=user, ).to_dict() return await self.patch( "/api/v2/polls/{poll_id}", PollResponse, path_params=path_params, json=json @@ -825,6 +860,8 @@ async def create_poll_option( self, poll_id: str, text: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, custom: Optional[Dict[str, object]] = None, user: Optional[UserRequest] = None, @@ -833,7 +870,12 @@ async def create_poll_option( "poll_id": poll_id, } json = CreatePollOptionRequest( - text=text, user_id=user_id, custom=custom, user=user + text=text, + config_key=config_key, + config_team=config_team, + user_id=user_id, + custom=custom, + user=user, ).to_dict() return await self.post( "/api/v2/polls/{poll_id}/options", @@ -848,6 +890,8 @@ async def update_poll_option( poll_id: str, id: str, text: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, custom: Optional[Dict[str, object]] = None, user: Optional[UserRequest] = None, @@ -856,7 +900,13 @@ async def update_poll_option( "poll_id": poll_id, } json = UpdatePollOptionRequest( - id=id, text=text, user_id=user_id, custom=custom, user=user + id=id, + text=text, + config_key=config_key, + config_team=config_team, + user_id=user_id, + custom=custom, + user=user, ).to_dict() return await self.put( "/api/v2/polls/{poll_id}/options", @@ -869,7 +919,7 @@ async def update_poll_option( async def delete_poll_option( self, poll_id: str, option_id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, "option_id": option_id, @@ -883,8 +933,9 @@ async def delete_poll_option( @telemetry.operation_name("getstream.api.common.get_poll_option") async def get_poll_option( - self, poll_id: str, option_id: str + self, poll_id: str, option_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollOptionResponse]: + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, "option_id": option_id, @@ -892,6 +943,7 @@ async def get_poll_option( return await self.get( "/api/v2/polls/{poll_id}/options/{option_id}", PollOptionResponse, + query_params=query_params, path_params=path_params, ) @@ -906,7 +958,7 @@ async def query_poll_votes( sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, ) -> StreamResponse[PollVotesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -962,7 +1014,10 @@ async def get_push_templates( self, push_provider_type: str, push_provider_name: Optional[str] = None ) -> StreamResponse[GetPushTemplatesResponse]: query_params = build_query_param( - push_provider_type=push_provider_type, push_provider_name=push_provider_name + **{ + "push_provider_type": push_provider_type, + "push_provider_name": push_provider_name, + } ) return await self.get( "/api/v2/push_templates", @@ -1003,14 +1058,16 @@ async def get_rate_limits( endpoints: Optional[str] = None, ) -> StreamResponse[GetRateLimitsResponse]: query_params = build_query_param( - server_side=server_side, - android=android, - ios=ios, - web=web, - unity=unity, - unity_desktop=unity_desktop, - unity_console=unity_console, - endpoints=endpoints, + **{ + "server_side": server_side, + "android": android, + "ios": ios, + "web": web, + "unity": unity, + "unity_desktop": unity_desktop, + "unity_console": unity_console, + "endpoints": endpoints, + } ) return await self.get( "/api/v2/rate_limits", GetRateLimitsResponse, query_params=query_params @@ -1035,11 +1092,13 @@ async def search_roles( include_global_roles: Optional[bool] = None, ) -> StreamResponse[SearchRolesResponse]: query_params = build_query_param( - query=query, - limit=limit, - name_gt=name_gt, - role_type=role_type, - include_global_roles=include_global_roles, + **{ + "query": query, + "limit": limit, + "name_gt": name_gt, + "role_type": role_type, + "include_global_roles": include_global_roles, + } ) return await self.get( "/api/v2/roles/search", SearchRolesResponse, query_params=query_params @@ -1065,7 +1124,7 @@ async def get_task(self, id: str) -> StreamResponse[GetTaskResponse]: @telemetry.operation_name("getstream.api.common.delete_file") async def delete_file(self, url: Optional[str] = None) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return await self.delete( "/api/v2/uploads/file", Response, query_params=query_params ) @@ -1075,11 +1134,13 @@ async def upload_file( self, file: Optional[str] = None, user: Optional[OnlyUserID] = None ) -> StreamResponse[FileUploadResponse]: json = FileUploadRequest(file=file, user=user).to_dict() - return await self.post("/api/v2/uploads/file", FileUploadResponse, json=json) + return await self._upload_multipart( + "/api/v2/uploads/file", FileUploadResponse, json=json + ) @telemetry.operation_name("getstream.api.common.delete_image") async def delete_image(self, url: Optional[str] = None) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return await self.delete( "/api/v2/uploads/image", Response, query_params=query_params ) @@ -1094,7 +1155,9 @@ async def upload_image( json = ImageUploadRequest( file=file, upload_sizes=upload_sizes, user=user ).to_dict() - return await self.post("/api/v2/uploads/image", ImageUploadResponse, json=json) + return await self._upload_multipart( + "/api/v2/uploads/image", ImageUploadResponse, json=json + ) @telemetry.operation_name("getstream.api.common.list_user_groups") async def list_user_groups( @@ -1105,7 +1168,12 @@ async def list_user_groups( team_id: Optional[str] = None, ) -> StreamResponse[ListUserGroupsResponse]: query_params = build_query_param( - limit=limit, id_gt=id_gt, created_at_gt=created_at_gt, team_id=team_id + **{ + "limit": limit, + "id_gt": id_gt, + "created_at_gt": created_at_gt, + "team_id": team_id, + } ) return await self.get( "/api/v2/usergroups", ListUserGroupsResponse, query_params=query_params @@ -1139,7 +1207,13 @@ async def search_user_groups( team_id: Optional[str] = None, ) -> StreamResponse[SearchUserGroupsResponse]: query_params = build_query_param( - query=query, limit=limit, name_gt=name_gt, id_gt=id_gt, team_id=team_id + **{ + "query": query, + "limit": limit, + "name_gt": name_gt, + "id_gt": id_gt, + "team_id": team_id, + } ) return await self.get( "/api/v2/usergroups/search", @@ -1151,7 +1225,7 @@ async def search_user_groups( async def delete_user_group( self, id: str, team_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(team_id=team_id) + query_params = build_query_param(**{"team_id": team_id}) path_params = { "id": id, } @@ -1166,7 +1240,7 @@ async def delete_user_group( async def get_user_group( self, id: str, team_id: Optional[str] = None ) -> StreamResponse[GetUserGroupResponse]: - query_params = build_query_param(team_id=team_id) + query_params = build_query_param(**{"team_id": team_id}) path_params = { "id": id, } @@ -1240,7 +1314,7 @@ async def remove_user_group_members( async def query_users( self, payload: Optional[QueryUsersPayload] = None ) -> StreamResponse[QueryUsersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return await self.get( "/api/v2/users", QueryUsersResponse, query_params=query_params ) @@ -1263,7 +1337,7 @@ async def update_users( async def get_blocked_users( self, user_id: Optional[str] = None ) -> StreamResponse[GetBlockedUsersResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return await self.get( "/api/v2/users/block", GetBlockedUsersResponse, query_params=query_params ) @@ -1326,7 +1400,7 @@ async def delete_users( async def get_user_live_locations( self, user_id: Optional[str] = None ) -> StreamResponse[SharedLocationsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return await self.get( "/api/v2/users/live_locations", SharedLocationsResponse, @@ -1342,7 +1416,7 @@ async def update_live_location( longitude: Optional[float] = None, user_id: Optional[str] = None, ) -> StreamResponse[SharedLocationResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = UpdateLiveLocationRequest( message_id=message_id, end_at=end_at, latitude=latitude, longitude=longitude ).to_dict() diff --git a/getstream/common/rest_client.py b/getstream/common/rest_client.py index 06405a58..bd447ef8 100644 --- a/getstream/common/rest_client.py +++ b/getstream/common/rest_client.py @@ -172,7 +172,9 @@ def list_block_lists( cursor: Optional[str] = None, limit: Optional[int] = None, ) -> StreamResponse[ListBlockListResponse]: - query_params = build_query_param(team=team, cursor=cursor, limit=limit) + query_params = build_query_param( + **{"team": team, "cursor": cursor, "limit": limit} + ) return self.get( "/api/v2/blocklists", ListBlockListResponse, query_params=query_params ) @@ -224,7 +226,7 @@ def import_block_list( def delete_block_list( self, name: str, team: Optional[str] = None, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(team=team, user_id=user_id) + query_params = build_query_param(**{"team": team, "user_id": user_id}) path_params = { "name": name, } @@ -239,7 +241,7 @@ def delete_block_list( def get_block_list( self, name: str, team: Optional[str] = None ) -> StreamResponse[GetBlockListResponse]: - query_params = build_query_param(team=team) + query_params = build_query_param(**{"team": team}) path_params = { "name": name, } @@ -283,6 +285,20 @@ def update_block_list( json=json, ) + @telemetry.operation_name("getstream.api.common.check_gcp_pub_sub") + def check_gcp_pub_sub( + self, + gcp_pubsub_event_based_ordering_key_enabled: Optional[bool] = None, + gcp_pubsub_region: Optional[str] = None, + gcp_pubsub_topic: Optional[str] = None, + ) -> StreamResponse[CheckGCPPubSubResponse]: + json = CheckGCPPubSubRequest( + gcp_pubsub_event_based_ordering_key_enabled=gcp_pubsub_event_based_ordering_key_enabled, + gcp_pubsub_region=gcp_pubsub_region, + gcp_pubsub_topic=gcp_pubsub_topic, + ).to_dict() + return self.post("/api/v2/check_gcp_pubsub", CheckGCPPubSubResponse, json=json) + @telemetry.operation_name("getstream.api.common.check_push") def check_push( self, @@ -339,14 +355,14 @@ def check_sqs( def delete_device( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(id=id, user_id=user_id) + query_params = build_query_param(**{"id": id, "user_id": user_id}) return self.delete("/api/v2/devices", Response, query_params=query_params) @telemetry.operation_name("getstream.api.common.list_devices") def list_devices( self, user_id: Optional[str] = None ) -> StreamResponse[ListDevicesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return self.get( "/api/v2/devices", ListDevicesResponse, query_params=query_params ) @@ -490,7 +506,7 @@ def create_import( def list_import_v2_tasks( self, state: Optional[int] = None ) -> StreamResponse[ListImportV2TasksResponse]: - query_params = build_query_param(state=state) + query_params = build_query_param(**{"state": state}) return self.get( "/api/v2/imports/v2", ListImportV2TasksResponse, query_params=query_params ) @@ -593,7 +609,7 @@ def get_import(self, id: str) -> StreamResponse[GetImportResponse]: @telemetry.operation_name("getstream.api.common.get_og") def get_og(self, url: str) -> StreamResponse[GetOGResponse]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return self.get("/api/v2/og", GetOGResponse, query_params=query_params) @telemetry.operation_name("getstream.api.common.list_permissions") @@ -674,11 +690,14 @@ def create_poll( name: str, allow_answers: Optional[bool] = None, allow_user_suggested_options: Optional[bool] = None, + config_key: Optional[str] = None, + config_team: Optional[str] = None, description: Optional[str] = None, enforce_unique_vote: Optional[bool] = None, id: Optional[str] = None, is_closed: Optional[bool] = None, max_votes_allowed: Optional[int] = None, + team: Optional[str] = None, user_id: Optional[str] = None, voting_visibility: Optional[str] = None, options: Optional[List[PollOptionInput]] = None, @@ -689,11 +708,14 @@ def create_poll( name=name, allow_answers=allow_answers, allow_user_suggested_options=allow_user_suggested_options, + config_key=config_key, + config_team=config_team, description=description, enforce_unique_vote=enforce_unique_vote, id=id, is_closed=is_closed, max_votes_allowed=max_votes_allowed, + team=team, user_id=user_id, voting_visibility=voting_visibility, options=options, @@ -709,6 +731,8 @@ def update_poll( name: str, allow_answers: Optional[bool] = None, allow_user_suggested_options: Optional[bool] = None, + config_key: Optional[str] = None, + config_team: Optional[str] = None, description: Optional[str] = None, enforce_unique_vote: Optional[bool] = None, is_closed: Optional[bool] = None, @@ -724,6 +748,8 @@ def update_poll( name=name, allow_answers=allow_answers, allow_user_suggested_options=allow_user_suggested_options, + config_key=config_key, + config_team=config_team, description=description, enforce_unique_vote=enforce_unique_vote, is_closed=is_closed, @@ -746,7 +772,7 @@ def query_polls( sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryPollsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = QueryPollsRequest( limit=limit, next=next, prev=prev, sort=sort, filter=filter ).to_dict() @@ -761,7 +787,7 @@ def query_polls( def delete_poll( self, poll_id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -776,7 +802,7 @@ def delete_poll( def get_poll( self, poll_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -791,6 +817,8 @@ def get_poll( def update_poll_partial( self, poll_id: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, unset: Optional[List[str]] = None, set: Optional[Dict[str, object]] = None, @@ -800,7 +828,12 @@ def update_poll_partial( "poll_id": poll_id, } json = UpdatePollPartialRequest( - user_id=user_id, unset=unset, set=set, user=user + config_key=config_key, + config_team=config_team, + user_id=user_id, + unset=unset, + set=set, + user=user, ).to_dict() return self.patch( "/api/v2/polls/{poll_id}", PollResponse, path_params=path_params, json=json @@ -811,6 +844,8 @@ def create_poll_option( self, poll_id: str, text: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, custom: Optional[Dict[str, object]] = None, user: Optional[UserRequest] = None, @@ -819,7 +854,12 @@ def create_poll_option( "poll_id": poll_id, } json = CreatePollOptionRequest( - text=text, user_id=user_id, custom=custom, user=user + text=text, + config_key=config_key, + config_team=config_team, + user_id=user_id, + custom=custom, + user=user, ).to_dict() return self.post( "/api/v2/polls/{poll_id}/options", @@ -834,6 +874,8 @@ def update_poll_option( poll_id: str, id: str, text: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, custom: Optional[Dict[str, object]] = None, user: Optional[UserRequest] = None, @@ -842,7 +884,13 @@ def update_poll_option( "poll_id": poll_id, } json = UpdatePollOptionRequest( - id=id, text=text, user_id=user_id, custom=custom, user=user + id=id, + text=text, + config_key=config_key, + config_team=config_team, + user_id=user_id, + custom=custom, + user=user, ).to_dict() return self.put( "/api/v2/polls/{poll_id}/options", @@ -855,7 +903,7 @@ def update_poll_option( def delete_poll_option( self, poll_id: str, option_id: str, user_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, "option_id": option_id, @@ -869,8 +917,9 @@ def delete_poll_option( @telemetry.operation_name("getstream.api.common.get_poll_option") def get_poll_option( - self, poll_id: str, option_id: str + self, poll_id: str, option_id: str, user_id: Optional[str] = None ) -> StreamResponse[PollOptionResponse]: + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, "option_id": option_id, @@ -878,6 +927,7 @@ def get_poll_option( return self.get( "/api/v2/polls/{poll_id}/options/{option_id}", PollOptionResponse, + query_params=query_params, path_params=path_params, ) @@ -892,7 +942,7 @@ def query_poll_votes( sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, ) -> StreamResponse[PollVotesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "poll_id": poll_id, } @@ -946,7 +996,10 @@ def get_push_templates( self, push_provider_type: str, push_provider_name: Optional[str] = None ) -> StreamResponse[GetPushTemplatesResponse]: query_params = build_query_param( - push_provider_type=push_provider_type, push_provider_name=push_provider_name + **{ + "push_provider_type": push_provider_type, + "push_provider_name": push_provider_name, + } ) return self.get( "/api/v2/push_templates", @@ -987,14 +1040,16 @@ def get_rate_limits( endpoints: Optional[str] = None, ) -> StreamResponse[GetRateLimitsResponse]: query_params = build_query_param( - server_side=server_side, - android=android, - ios=ios, - web=web, - unity=unity, - unity_desktop=unity_desktop, - unity_console=unity_console, - endpoints=endpoints, + **{ + "server_side": server_side, + "android": android, + "ios": ios, + "web": web, + "unity": unity, + "unity_desktop": unity_desktop, + "unity_console": unity_console, + "endpoints": endpoints, + } ) return self.get( "/api/v2/rate_limits", GetRateLimitsResponse, query_params=query_params @@ -1019,11 +1074,13 @@ def search_roles( include_global_roles: Optional[bool] = None, ) -> StreamResponse[SearchRolesResponse]: query_params = build_query_param( - query=query, - limit=limit, - name_gt=name_gt, - role_type=role_type, - include_global_roles=include_global_roles, + **{ + "query": query, + "limit": limit, + "name_gt": name_gt, + "role_type": role_type, + "include_global_roles": include_global_roles, + } ) return self.get( "/api/v2/roles/search", SearchRolesResponse, query_params=query_params @@ -1045,7 +1102,7 @@ def get_task(self, id: str) -> StreamResponse[GetTaskResponse]: @telemetry.operation_name("getstream.api.common.delete_file") def delete_file(self, url: Optional[str] = None) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return self.delete("/api/v2/uploads/file", Response, query_params=query_params) @telemetry.operation_name("getstream.api.common.upload_file") @@ -1053,11 +1110,13 @@ def upload_file( self, file: Optional[str] = None, user: Optional[OnlyUserID] = None ) -> StreamResponse[FileUploadResponse]: json = FileUploadRequest(file=file, user=user).to_dict() - return self.post("/api/v2/uploads/file", FileUploadResponse, json=json) + return self._upload_multipart( + "/api/v2/uploads/file", FileUploadResponse, json=json + ) @telemetry.operation_name("getstream.api.common.delete_image") def delete_image(self, url: Optional[str] = None) -> StreamResponse[Response]: - query_params = build_query_param(url=url) + query_params = build_query_param(**{"url": url}) return self.delete("/api/v2/uploads/image", Response, query_params=query_params) @telemetry.operation_name("getstream.api.common.upload_image") @@ -1070,7 +1129,9 @@ def upload_image( json = ImageUploadRequest( file=file, upload_sizes=upload_sizes, user=user ).to_dict() - return self.post("/api/v2/uploads/image", ImageUploadResponse, json=json) + return self._upload_multipart( + "/api/v2/uploads/image", ImageUploadResponse, json=json + ) @telemetry.operation_name("getstream.api.common.list_user_groups") def list_user_groups( @@ -1081,7 +1142,12 @@ def list_user_groups( team_id: Optional[str] = None, ) -> StreamResponse[ListUserGroupsResponse]: query_params = build_query_param( - limit=limit, id_gt=id_gt, created_at_gt=created_at_gt, team_id=team_id + **{ + "limit": limit, + "id_gt": id_gt, + "created_at_gt": created_at_gt, + "team_id": team_id, + } ) return self.get( "/api/v2/usergroups", ListUserGroupsResponse, query_params=query_params @@ -1115,7 +1181,13 @@ def search_user_groups( team_id: Optional[str] = None, ) -> StreamResponse[SearchUserGroupsResponse]: query_params = build_query_param( - query=query, limit=limit, name_gt=name_gt, id_gt=id_gt, team_id=team_id + **{ + "query": query, + "limit": limit, + "name_gt": name_gt, + "id_gt": id_gt, + "team_id": team_id, + } ) return self.get( "/api/v2/usergroups/search", @@ -1127,7 +1199,7 @@ def search_user_groups( def delete_user_group( self, id: str, team_id: Optional[str] = None ) -> StreamResponse[Response]: - query_params = build_query_param(team_id=team_id) + query_params = build_query_param(**{"team_id": team_id}) path_params = { "id": id, } @@ -1142,7 +1214,7 @@ def delete_user_group( def get_user_group( self, id: str, team_id: Optional[str] = None ) -> StreamResponse[GetUserGroupResponse]: - query_params = build_query_param(team_id=team_id) + query_params = build_query_param(**{"team_id": team_id}) path_params = { "id": id, } @@ -1216,7 +1288,7 @@ def remove_user_group_members( def query_users( self, payload: Optional[QueryUsersPayload] = None ) -> StreamResponse[QueryUsersResponse]: - query_params = build_query_param(payload=payload) + query_params = build_query_param(**{"payload": payload}) return self.get("/api/v2/users", QueryUsersResponse, query_params=query_params) @telemetry.operation_name("getstream.api.common.update_users_partial") @@ -1237,7 +1309,7 @@ def update_users( def get_blocked_users( self, user_id: Optional[str] = None ) -> StreamResponse[GetBlockedUsersResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return self.get( "/api/v2/users/block", GetBlockedUsersResponse, query_params=query_params ) @@ -1298,7 +1370,7 @@ def delete_users( def get_user_live_locations( self, user_id: Optional[str] = None ) -> StreamResponse[SharedLocationsResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return self.get( "/api/v2/users/live_locations", SharedLocationsResponse, @@ -1314,7 +1386,7 @@ def update_live_location( longitude: Optional[float] = None, user_id: Optional[str] = None, ) -> StreamResponse[SharedLocationResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) json = UpdateLiveLocationRequest( message_id=message_id, end_at=end_at, latitude=latitude, longitude=longitude ).to_dict() diff --git a/getstream/feeds/feeds.py b/getstream/feeds/feeds.py index 3ad3d7b5..7578821b 100644 --- a/getstream/feeds/feeds.py +++ b/getstream/feeds/feeds.py @@ -40,6 +40,7 @@ def get_or_create( user_id: Optional[str] = None, view: Optional[str] = None, watch: Optional[bool] = None, + discard_actors: Optional[List[str]] = None, data: Optional[FeedInput] = None, enrichment_options: Optional[EnrichmentOptions] = None, external_ranking: Optional[Dict[str, object]] = None, @@ -64,6 +65,7 @@ def get_or_create( user_id=user_id, view=view, watch=watch, + discard_actors=discard_actors, data=data, enrichment_options=enrichment_options, external_ranking=external_ranking, @@ -244,11 +246,16 @@ def query_pinned_activities( language: Optional[str] = None, translate_text: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, + include_expired_activities: Optional[bool] = None, + include_moderated_activities: Optional[bool] = None, + include_soft_deleted_activities: Optional[bool] = None, limit: Optional[int] = None, next: Optional[str] = None, prev: Optional[str] = None, + user_id: Optional[str] = None, sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, ) -> StreamResponse[QueryPinnedActivitiesResponse]: response = self.client.query_pinned_activities( feed_group_id=self.feed_group, @@ -256,11 +263,16 @@ def query_pinned_activities( language=language, translate_text=translate_text, enrich_own_fields=enrich_own_fields, + include_expired_activities=include_expired_activities, + include_moderated_activities=include_moderated_activities, + include_soft_deleted_activities=include_soft_deleted_activities, limit=limit, next=next, prev=prev, + user_id=user_id, sort=sort, filter=filter, + user=user, ) self._sync_from_response(response.data) return response diff --git a/getstream/feeds/rest_client.py b/getstream/feeds/rest_client.py index cd5c6b47..6817a268 100644 --- a/getstream/feeds/rest_client.py +++ b/getstream/feeds/rest_client.py @@ -47,6 +47,7 @@ def add_activity( poll_id: Optional[str] = None, restrict_replies: Optional[str] = None, skip_enrich_url: Optional[bool] = None, + skip_existing: Optional[bool] = None, skip_push: Optional[bool] = None, text: Optional[str] = None, user_id: Optional[str] = None, @@ -76,6 +77,7 @@ def add_activity( poll_id=poll_id, restrict_replies=restrict_replies, skip_enrich_url=skip_enrich_url, + skip_existing=skip_existing, skip_push=skip_push, text=text, user_id=user_id, @@ -100,12 +102,14 @@ def upsert_activities( create_users: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, force_moderation: Optional[bool] = None, + skip_existing: Optional[bool] = None, ) -> StreamResponse[UpsertActivitiesResponse]: json = UpsertActivitiesRequest( activities=activities, create_users=create_users, enrich_own_fields=enrich_own_fields, force_moderation=force_moderation, + skip_existing=skip_existing, ).to_dict() return self.post( "/api/v2/feeds/activities/batch", UpsertActivitiesResponse, json=json @@ -169,6 +173,7 @@ def query_activities( translate_text: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, include_expired_activities: Optional[bool] = None, + include_moderated_activities: Optional[bool] = None, include_private_activities: Optional[bool] = None, include_soft_deleted_activities: Optional[bool] = None, limit: Optional[int] = None, @@ -180,11 +185,12 @@ def query_activities( user: Optional[UserRequest] = None, ) -> StreamResponse[QueryActivitiesResponse]: query_params = build_query_param( - language=language, translate_text=translate_text + **{"language": language, "translate_text": translate_text} ) json = QueryActivitiesRequest( enrich_own_fields=enrich_own_fields, include_expired_activities=include_expired_activities, + include_moderated_activities=include_moderated_activities, include_private_activities=include_private_activities, include_soft_deleted_activities=include_soft_deleted_activities, limit=limit, @@ -237,7 +243,7 @@ def delete_bookmark( folder_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[DeleteBookmarkResponse]: - query_params = build_query_param(folder_id=folder_id, user_id=user_id) + query_params = build_query_param(**{"folder_id": folder_id, "user_id": user_id}) path_params = { "activity_id": activity_id, } @@ -336,6 +342,8 @@ def cast_poll_vote( self, activity_id: str, poll_id: str, + config_key: Optional[str] = None, + config_team: Optional[str] = None, user_id: Optional[str] = None, user: Optional[UserRequest] = None, vote: Optional[VoteData] = None, @@ -344,7 +352,13 @@ def cast_poll_vote( "activity_id": activity_id, "poll_id": poll_id, } - json = CastPollVoteRequest(user_id=user_id, user=user, vote=vote).to_dict() + json = CastPollVoteRequest( + config_key=config_key, + config_team=config_team, + user_id=user_id, + user=user, + vote=vote, + ).to_dict() return self.post( "/api/v2/feeds/activities/{activity_id}/polls/{poll_id}/vote", PollVoteResponse, @@ -360,7 +374,7 @@ def delete_poll_vote( vote_id: str, user_id: Optional[str] = None, ) -> StreamResponse[PollVoteResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "activity_id": activity_id, "poll_id": poll_id, @@ -442,7 +456,10 @@ def delete_activity_reaction( user_id: Optional[str] = None, ) -> StreamResponse[DeleteActivityReactionResponse]: query_params = build_query_param( - delete_notification_activity=delete_notification_activity, user_id=user_id + **{ + "delete_notification_activity": delete_notification_activity, + "user_id": user_id, + } ) path_params = { "activity_id": activity_id, @@ -463,7 +480,7 @@ def query_activity_shares( prev: Optional[str] = None, next: Optional[str] = None, ) -> StreamResponse[QueryActivitySharesResponse]: - query_params = build_query_param(limit=limit, prev=prev, next=next) + query_params = build_query_param(**{"limit": limit, "prev": prev, "next": next}) path_params = { "activity_id": activity_id, } @@ -482,8 +499,10 @@ def delete_activity( delete_notification_activity: Optional[bool] = None, ) -> StreamResponse[DeleteActivityResponse]: query_params = build_query_param( - hard_delete=hard_delete, - delete_notification_activity=delete_notification_activity, + **{ + "hard_delete": hard_delete, + "delete_notification_activity": delete_notification_activity, + } ) path_params = { "id": id, @@ -501,16 +520,26 @@ def get_activity( id: str, comment_sort: Optional[str] = None, comment_limit: Optional[int] = None, + skip_own_followings: Optional[bool] = None, user_id: Optional[str] = None, language: Optional[str] = None, translate_text: Optional[bool] = None, + include_top_level_comment_count: Optional[bool] = None, + include_soft_deleted_activities: Optional[bool] = None, + include_moderated_activities: Optional[bool] = None, ) -> StreamResponse[GetActivityResponse]: query_params = build_query_param( - comment_sort=comment_sort, - comment_limit=comment_limit, - user_id=user_id, - language=language, - translate_text=translate_text, + **{ + "comment_sort": comment_sort, + "comment_limit": comment_limit, + "skip_own_followings": skip_own_followings, + "user_id": user_id, + "language": language, + "translate_text": translate_text, + "include_top_level_comment_count": include_top_level_comment_count, + "include_soft_deleted_activities": include_soft_deleted_activities, + "include_moderated_activities": include_moderated_activities, + } ) path_params = { "id": id, @@ -628,7 +657,7 @@ def restore_activity( user_id: Optional[str] = None, user: Optional[UserRequest] = None, ) -> StreamResponse[RestoreActivityResponse]: - query_params = build_query_param(enrich_own_fields=enrich_own_fields) + query_params = build_query_param(**{"enrich_own_fields": enrich_own_fields}) path_params = { "id": id, } @@ -724,7 +753,7 @@ def query_bookmarks( user: Optional[UserRequest] = None, ) -> StreamResponse[QueryBookmarksResponse]: query_params = build_query_param( - language=language, translate_text=translate_text + **{"language": language, "translate_text": translate_text} ) json = QueryBookmarksRequest( enrich_own_fields=enrich_own_fields, @@ -745,9 +774,11 @@ def query_bookmarks( @telemetry.operation_name("getstream.api.feeds.delete_collections") def delete_collections( - self, collection_refs: List[str] + self, collection_refs: List[str], hard_delete: Optional[bool] = None ) -> StreamResponse[DeleteCollectionsResponse]: - query_params = build_query_param(collection_refs=collection_refs) + query_params = build_query_param( + **{"collection_refs": collection_refs, "hard_delete": hard_delete} + ) return self.delete( "/api/v2/feeds/collections", DeleteCollectionsResponse, @@ -759,7 +790,7 @@ def read_collections( self, user_id: Optional[str] = None, collection_refs: Optional[List[str]] = None ) -> StreamResponse[ReadCollectionsResponse]: query_params = build_query_param( - user_id=user_id, collection_refs=collection_refs + **{"user_id": user_id, "collection_refs": collection_refs} ) return self.get( "/api/v2/feeds/collections", @@ -843,20 +874,24 @@ def get_comments( limit: Optional[int] = None, prev: Optional[str] = None, next: Optional[str] = None, + include_top_level_comment_count: Optional[bool] = None, ) -> StreamResponse[GetCommentsResponse]: query_params = build_query_param( - object_id=object_id, - object_type=object_type, - depth=depth, - sort=sort, - replies_limit=replies_limit, - id_around=id_around, - language=language, - translate_text=translate_text, - user_id=user_id, - limit=limit, - prev=prev, - next=next, + **{ + "object_id": object_id, + "object_type": object_type, + "depth": depth, + "sort": sort, + "replies_limit": replies_limit, + "id_around": id_around, + "language": language, + "translate_text": translate_text, + "user_id": user_id, + "limit": limit, + "prev": prev, + "next": next, + "include_top_level_comment_count": include_top_level_comment_count, + } ) return self.get( "/api/v2/feeds/comments", GetCommentsResponse, query_params=query_params @@ -914,6 +949,7 @@ def query_comments( self, filter: Dict[str, object], id_around: Optional[str] = None, + include_soft_deleted_comments: Optional[bool] = None, limit: Optional[int] = None, next: Optional[str] = None, prev: Optional[str] = None, @@ -924,11 +960,12 @@ def query_comments( translate_text: Optional[bool] = None, ) -> StreamResponse[QueryCommentsResponse]: query_params = build_query_param( - language=language, translate_text=translate_text + **{"language": language, "translate_text": translate_text} ) json = QueryCommentsRequest( filter=filter, id_around=id_around, + include_soft_deleted_comments=include_soft_deleted_comments, limit=limit, next=next, prev=prev, @@ -978,7 +1015,7 @@ def delete_comment_bookmark( folder_id: Optional[str] = None, user_id: Optional[str] = None, ) -> StreamResponse[DeleteCommentBookmarkResponse]: - query_params = build_query_param(folder_id=folder_id, user_id=user_id) + query_params = build_query_param(**{"folder_id": folder_id, "user_id": user_id}) path_params = { "comment_id": comment_id, } @@ -1053,8 +1090,10 @@ def delete_comment( delete_notification_activity: Optional[bool] = None, ) -> StreamResponse[DeleteCommentResponse]: query_params = build_query_param( - hard_delete=hard_delete, - delete_notification_activity=delete_notification_activity, + **{ + "hard_delete": hard_delete, + "delete_notification_activity": delete_notification_activity, + } ) path_params = { "id": id, @@ -1075,7 +1114,11 @@ def get_comment( translate_text: Optional[bool] = None, ) -> StreamResponse[GetCommentResponse]: query_params = build_query_param( - user_id=user_id, language=language, translate_text=translate_text + **{ + "user_id": user_id, + "language": language, + "translate_text": translate_text, + } ) path_params = { "id": id, @@ -1126,6 +1169,25 @@ def update_comment( json=json, ) + @telemetry.operation_name("getstream.api.feeds.comment_feedback") + def comment_feedback( + self, + id: str, + hide: Optional[bool] = None, + user_id: Optional[str] = None, + user: Optional[UserRequest] = None, + ) -> StreamResponse[CommentFeedbackResponse]: + path_params = { + "id": id, + } + json = CommentFeedbackRequest(hide=hide, user_id=user_id, user=user).to_dict() + return self.post( + "/api/v2/feeds/comments/{id}/feedback", + CommentFeedbackResponse, + path_params=path_params, + json=json, + ) + @telemetry.operation_name("getstream.api.feeds.update_comment_partial") def update_comment_partial( self, @@ -1228,7 +1290,10 @@ def delete_comment_reaction( user_id: Optional[str] = None, ) -> StreamResponse[DeleteCommentReactionResponse]: query_params = build_query_param( - delete_notification_activity=delete_notification_activity, user_id=user_id + **{ + "delete_notification_activity": delete_notification_activity, + "user_id": user_id, + } ) path_params = { "id": id, @@ -1257,16 +1322,18 @@ def get_comment_replies( next: Optional[str] = None, ) -> StreamResponse[GetCommentRepliesResponse]: query_params = build_query_param( - depth=depth, - sort=sort, - replies_limit=replies_limit, - id_around=id_around, - language=language, - translate_text=translate_text, - user_id=user_id, - limit=limit, - prev=prev, - next=next, + **{ + "depth": depth, + "sort": sort, + "replies_limit": replies_limit, + "id_around": id_around, + "language": language, + "translate_text": translate_text, + "user_id": user_id, + "limit": limit, + "prev": prev, + "next": next, + } ) path_params = { "id": id, @@ -1312,7 +1379,9 @@ def translate_comment( def list_feed_groups( self, include_soft_deleted: Optional[bool] = None ) -> StreamResponse[ListFeedGroupsResponse]: - query_params = build_query_param(include_soft_deleted=include_soft_deleted) + query_params = build_query_param( + **{"include_soft_deleted": include_soft_deleted} + ) return self.get( "/api/v2/feeds/feed_groups", ListFeedGroupsResponse, @@ -1366,7 +1435,10 @@ def delete_feed( purge_user_activities: Optional[bool] = None, ) -> StreamResponse[DeleteFeedResponse]: query_params = build_query_param( - hard_delete=hard_delete, purge_user_activities=purge_user_activities + **{ + "hard_delete": hard_delete, + "purge_user_activities": purge_user_activities, + } ) path_params = { "feed_group_id": feed_group_id, @@ -1394,6 +1466,7 @@ def get_or_create_feed( user_id: Optional[str] = None, view: Optional[str] = None, watch: Optional[bool] = None, + discard_actors: Optional[List[str]] = None, data: Optional[FeedInput] = None, enrichment_options: Optional[EnrichmentOptions] = None, external_ranking: Optional[Dict[str, object]] = None, @@ -1406,7 +1479,7 @@ def get_or_create_feed( user: Optional[UserRequest] = None, ) -> StreamResponse[GetOrCreateFeedResponse]: query_params = build_query_param( - language=language, translate_text=translate_text + **{"language": language, "translate_text": translate_text} ) path_params = { "feed_group_id": feed_group_id, @@ -1421,6 +1494,7 @@ def get_or_create_feed( user_id=user_id, view=view, watch=watch, + discard_actors=discard_actors, data=data, enrichment_options=enrichment_options, external_ranking=external_ranking, @@ -1518,7 +1592,7 @@ def unpin_activity( user_id: Optional[str] = None, ) -> StreamResponse[UnpinActivityResponse]: query_params = build_query_param( - enrich_own_fields=enrich_own_fields, user_id=user_id + **{"enrich_own_fields": enrich_own_fields, "user_id": user_id} ) path_params = { "feed_group_id": feed_group_id, @@ -1691,14 +1765,19 @@ def query_pinned_activities( language: Optional[str] = None, translate_text: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, + include_expired_activities: Optional[bool] = None, + include_moderated_activities: Optional[bool] = None, + include_soft_deleted_activities: Optional[bool] = None, limit: Optional[int] = None, next: Optional[str] = None, prev: Optional[str] = None, + user_id: Optional[str] = None, sort: Optional[List[SortParamRequest]] = None, filter: Optional[Dict[str, object]] = None, + user: Optional[UserRequest] = None, ) -> StreamResponse[QueryPinnedActivitiesResponse]: query_params = build_query_param( - language=language, translate_text=translate_text + **{"language": language, "translate_text": translate_text} ) path_params = { "feed_group_id": feed_group_id, @@ -1706,11 +1785,16 @@ def query_pinned_activities( } json = QueryPinnedActivitiesRequest( enrich_own_fields=enrich_own_fields, + include_expired_activities=include_expired_activities, + include_moderated_activities=include_moderated_activities, + include_soft_deleted_activities=include_soft_deleted_activities, limit=limit, next=next, prev=prev, + user_id=user_id, sort=sort, filter=filter, + user=user, ).to_dict() return self.post( "/api/v2/feeds/feed_groups/{feed_group_id}/feeds/{feed_id}/pinned_activities/query", @@ -1727,7 +1811,7 @@ def get_follow_suggestions( limit: Optional[int] = None, user_id: Optional[str] = None, ) -> StreamResponse[GetFollowSuggestionsResponse]: - query_params = build_query_param(limit=limit, user_id=user_id) + query_params = build_query_param(**{"limit": limit, "user_id": user_id}) path_params = { "feed_group_id": feed_group_id, } @@ -1755,7 +1839,7 @@ def restore_feed_group( def delete_feed_group( self, id: str, hard_delete: Optional[bool] = None ) -> StreamResponse[DeleteFeedGroupResponse]: - query_params = build_query_param(hard_delete=hard_delete) + query_params = build_query_param(**{"hard_delete": hard_delete}) path_params = { "id": id, } @@ -1770,7 +1854,9 @@ def delete_feed_group( def get_feed_group( self, id: str, include_soft_deleted: Optional[bool] = None ) -> StreamResponse[GetFeedGroupResponse]: - query_params = build_query_param(include_soft_deleted=include_soft_deleted) + query_params = build_query_param( + **{"include_soft_deleted": include_soft_deleted} + ) path_params = { "id": id, } @@ -2065,14 +2151,16 @@ def get_feeds_rate_limits( server_side: Optional[bool] = None, ) -> StreamResponse[GetFeedsRateLimitsResponse]: query_params = build_query_param( - endpoints=endpoints, - android=android, - ios=ios, - web=web, - unity=unity, - unity_desktop=unity_desktop, - unity_console=unity_console, - server_side=server_side, + **{ + "endpoints": endpoints, + "android": android, + "ios": ios, + "web": web, + "unity": unity, + "unity_desktop": unity_desktop, + "unity_console": unity_console, + "server_side": server_side, + } ) return self.get( "/api/v2/feeds/feeds/rate_limits", @@ -2157,11 +2245,13 @@ def accept_follow( def follow_batch( self, follows: List[FollowRequest], + _async: Optional[bool] = None, create_users: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, ) -> StreamResponse[FollowBatchResponse]: json = FollowBatchRequest( follows=follows, + _async=_async, create_users=create_users, enrich_own_fields=enrich_own_fields, ).to_dict() @@ -2171,11 +2261,13 @@ def follow_batch( def get_or_create_follows( self, follows: List[FollowRequest], + _async: Optional[bool] = None, create_users: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, ) -> StreamResponse[FollowBatchResponse]: json = FollowBatchRequest( follows=follows, + _async=_async, create_users=create_users, enrich_own_fields=enrich_own_fields, ).to_dict() @@ -2186,6 +2278,7 @@ def get_or_create_follows( @telemetry.operation_name("getstream.api.feeds.query_follows") def query_follows( self, + include_group_counts: Optional[bool] = None, limit: Optional[int] = None, next: Optional[str] = None, prev: Optional[str] = None, @@ -2193,7 +2286,12 @@ def query_follows( filter: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryFollowsResponse]: json = QueryFollowsRequest( - limit=limit, next=next, prev=prev, sort=sort, filter=filter + include_group_counts=include_group_counts, + limit=limit, + next=next, + prev=prev, + sort=sort, + filter=filter, ).to_dict() return self.post("/api/v2/feeds/follows/query", QueryFollowsResponse, json=json) @@ -2248,9 +2346,11 @@ def unfollow( enrich_own_fields: Optional[bool] = None, ) -> StreamResponse[UnfollowResponse]: query_params = build_query_param( - delete_notification_activity=delete_notification_activity, - keep_history=keep_history, - enrich_own_fields=enrich_own_fields, + **{ + "delete_notification_activity": delete_notification_activity, + "keep_history": keep_history, + "enrich_own_fields": enrich_own_fields, + } ) path_params = { "source": source, @@ -2339,6 +2439,65 @@ def update_membership_level( json=json, ) + @telemetry.operation_name("getstream.api.feeds.feeds_get_retention_policy") + def feeds_get_retention_policy( + self, + ) -> StreamResponse[GetFeedsRetentionPolicyResponse]: + return self.get( + "/api/v2/feeds/retention_policy", GetFeedsRetentionPolicyResponse + ) + + @telemetry.operation_name("getstream.api.feeds.feeds_set_retention_policy") + def feeds_set_retention_policy( + self, + max_age_hours: int, + policy: str, + enabled: Optional[bool] = None, + feed_groups: Optional[List[str]] = None, + ) -> StreamResponse[SetFeedsRetentionPolicyResponse]: + json = SetFeedsRetentionPolicyRequest( + max_age_hours=max_age_hours, + policy=policy, + enabled=enabled, + feed_groups=feed_groups, + ).to_dict() + return self.post( + "/api/v2/feeds/retention_policy", SetFeedsRetentionPolicyResponse, json=json + ) + + @telemetry.operation_name("getstream.api.feeds.feeds_delete_retention_policy") + def feeds_delete_retention_policy( + self, policy: str + ) -> StreamResponse[DeleteFeedsRetentionPolicyResponse]: + json = DeleteFeedsRetentionPolicyRequest(policy=policy).to_dict() + return self.post( + "/api/v2/feeds/retention_policy/delete", + DeleteFeedsRetentionPolicyResponse, + json=json, + ) + + @telemetry.operation_name("getstream.api.feeds.feeds_get_retention_policy_runs") + def feeds_get_retention_policy_runs( + self, + limit: Optional[int] = None, + next: Optional[str] = None, + prev: Optional[str] = None, + sort: Optional[List[SortParamRequest]] = None, + filter_conditions: Optional[Dict[str, object]] = None, + ) -> StreamResponse[GetFeedsRetentionPolicyRunsResponse]: + json = GetFeedsRetentionPolicyRunsRequest( + limit=limit, + next=next, + prev=prev, + sort=sort, + filter_conditions=filter_conditions, + ).to_dict() + return self.post( + "/api/v2/feeds/retention_policy/runs", + GetFeedsRetentionPolicyRunsResponse, + json=json, + ) + @telemetry.operation_name("getstream.api.feeds.query_revision_history") def query_revision_history( self, @@ -2368,11 +2527,13 @@ def query_feeds_usage_stats( def unfollow_batch( self, follows: List[UnfollowPair], + _async: Optional[bool] = None, delete_notification_activity: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, ) -> StreamResponse[UnfollowBatchResponse]: json = UnfollowBatchRequest( follows=follows, + _async=_async, delete_notification_activity=delete_notification_activity, enrich_own_fields=enrich_own_fields, ).to_dict() @@ -2384,11 +2545,13 @@ def unfollow_batch( def get_or_create_unfollows( self, follows: List[UnfollowPair], + _async: Optional[bool] = None, delete_notification_activity: Optional[bool] = None, enrich_own_fields: Optional[bool] = None, ) -> StreamResponse[UnfollowBatchResponse]: json = UnfollowBatchRequest( follows=follows, + _async=_async, delete_notification_activity=delete_notification_activity, enrich_own_fields=enrich_own_fields, ).to_dict() @@ -2444,11 +2607,26 @@ def export_feed_user_data( path_params=path_params, ) + @telemetry.operation_name("getstream.api.feeds.delete_user_interests") + def delete_user_interests( + self, user_id: str, tags: List[str] + ) -> StreamResponse[DeleteUserInterestsResponse]: + query_params = build_query_param(**{"tags": tags}) + path_params = { + "user_id": user_id, + } + return self.delete( + "/api/v2/feeds/users/{user_id}/interests", + DeleteUserInterestsResponse, + query_params=query_params, + path_params=path_params, + ) + @telemetry.operation_name("getstream.api.feeds.get_user_interests") def get_user_interests( self, user_id: str, limit: Optional[int] = None ) -> StreamResponse[GetUserInterestsResponse]: - query_params = build_query_param(limit=limit) + query_params = build_query_param(**{"limit": limit}) path_params = { "user_id": user_id, } @@ -2458,3 +2636,18 @@ def get_user_interests( query_params=query_params, path_params=path_params, ) + + @telemetry.operation_name("getstream.api.feeds.upsert_user_interests") + def upsert_user_interests( + self, user_id: str, interests: List[UserInterestRequest] + ) -> StreamResponse[UpsertUserInterestsResponse]: + path_params = { + "user_id": user_id, + } + json = UpsertUserInterestsRequest(interests=interests).to_dict() + return self.put( + "/api/v2/feeds/users/{user_id}/interests", + UpsertUserInterestsResponse, + path_params=path_params, + json=json, + ) diff --git a/getstream/models/__init__.py b/getstream/models/__init__.py index 12d28cd4..185544ac 100644 --- a/getstream/models/__init__.py +++ b/getstream/models/__init__.py @@ -1,31 +1,46 @@ # Code generated by GetStream internal OpenAPI code generator. DO NOT EDIT. from dataclasses import dataclass from dataclasses import field as dc_field -from datetime import datetime -from typing import Dict, Final, List, NewType, Optional - from dataclasses_json import DataClassJsonMixin from dataclasses_json import config as dc_config +from datetime import datetime from marshmallow import fields - -from getstream.utils import datetime_from_unix_ns, encode_datetime +from typing import List, Dict, Optional, Final, NewType +from getstream.utils import encode_datetime, datetime_from_unix_ns @dataclass class AIAudioConfigRequest(DataClassJsonMixin): + # Bodyguard credentials profile to classify transcripts with. Applies to the NLP engine only; ignored when LLM configurability is enabled. Empty falls back to the app-pinned profile, then the organization's top-level credentials profile: str | None = dc_field( default=None, metadata=dc_config(field_name="profile") ) + # Deprecated pre-split rule list. Still the live config while no engine config exists; once one does it is kept as a fallback for whichever engine has none of its own rules: "list[BodyguardRule] | None" = dc_field( default=None, metadata=dc_config(field_name="rules") ) + ai_text_config: "AITextConfig | None" = dc_field( + default=None, metadata=dc_config(field_name="ai_text_config") + ) + llm_config: "LLMConfig | None" = dc_field( + default=None, metadata=dc_config(field_name="llm_config") + ) @dataclass class AIAudioConfigResponse(DataClassJsonMixin): + # Whether the engine selected by the application's LLM configurability flag has a rule that does something. Derived, not stored enabled: bool = dc_field(metadata=dc_config(field_name="enabled")) + # Bodyguard credentials profile transcripts are classified with. Applies to the NLP engine only profile: str = dc_field(metadata=dc_config(field_name="profile")) + # The active engine's rules, flattened into the pre-split shape for clients that predate the per-engine configs. This is the whole config for a policy that has no per-engine config stored rules: "list[BodyguardRule]" = dc_field(metadata=dc_config(field_name="rules")) + ai_text_config: "AITextConfig | None" = dc_field( + default=None, metadata=dc_config(field_name="ai_text_config") + ) + llm_config: "LLMConfig | None" = dc_field( + default=None, metadata=dc_config(field_name="llm_config") + ) @dataclass @@ -73,9 +88,6 @@ class AIVideoConfig(DataClassJsonMixin): enabled: bool | None = dc_field( default=None, metadata=dc_config(field_name="enabled") ) - provider: str | None = dc_field( - default=None, metadata=dc_config(field_name="provider") - ) rules: "list[AWSRekognitionRule] | None" = dc_field( default=None, metadata=dc_config(field_name="rules") ) @@ -113,6 +125,10 @@ class APIError(DataClassJsonMixin): status_code: int = dc_field(metadata=dc_config(field_name="StatusCode")) # Additional error-specific information details: "list[int]" = dc_field(metadata=dc_config(field_name="details")) + # Milliseconds until the user's slow mode cool down period ends. Only set on code 60 (cool down) errors + cooldown_remaining_ms: int | None = dc_field( + default=None, metadata=dc_config(field_name="cooldown_remaining_ms") + ) # Flag that indicates if the error is unrecoverable, requests that return unrecoverable errors should not be retried, this error only applies to the request that caused it unrecoverable: bool | None = dc_field( default=None, metadata=dc_config(field_name="unrecoverable") @@ -1098,6 +1114,10 @@ class ActivityResponse(DataClassJsonMixin): ) # Text content of the activity text: str | None = dc_field(default=None, metadata=dc_config(field_name="text")) + # Number of top-level comments (comments directly on the activity, excluding replies). Only returned by GetActivity when include_top_level_comment_count=true; never set on feed reads or events. Same status/deletion rules as comment_count; not adjusted per viewer. + top_level_comment_count: int | None = dc_field( + default=None, metadata=dc_config(field_name="top_level_comment_count") + ) # If visibility is 'tag', this is the tag name visibility_tag: str | None = dc_field( default=None, metadata=dc_config(field_name="visibility_tag") @@ -1191,6 +1211,14 @@ class ActivitySelectorConfig(DataClassJsonMixin): min_popularity: int | None = dc_field( default=None, metadata=dc_config(field_name="min_popularity") ) + # Maximum number of candidate activities this selector contributes to a ranked feed (1-1000). Omit to use the default ranking buffer. Only supported on the popular, proximity, interest and query selectors, and ignored on feeds without ranking + ranking_candidate_limit: int | None = dc_field( + default=None, metadata=dc_config(field_name="ranking_candidate_limit") + ) + # Maximum time in milliseconds this selector may take (25-5000). A selector that runs over its budget is dropped from that read instead of failing it, and stays out for the rest of that pagination session. The budget can only tighten the default, never extend it. Omit to use the default. Only supported on the popular, proximity, interest, query and follow_suggestion selectors + time_budget_ms: int | None = dc_field( + default=None, metadata=dc_config(field_name="time_budget_ms") + ) # Sort parameters for activity selection sort: "list[SortParamRequest] | None" = dc_field( default=None, metadata=dc_config(field_name="sort") @@ -1229,6 +1257,14 @@ class ActivitySelectorConfigResponse(DataClassJsonMixin): min_popularity: int | None = dc_field( default=None, metadata=dc_config(field_name="min_popularity") ) + # Maximum number of candidate activities this selector contributes to a ranked feed + ranking_candidate_limit: int | None = dc_field( + default=None, metadata=dc_config(field_name="ranking_candidate_limit") + ) + # Maximum time in milliseconds this selector may take before it is dropped from the read + time_budget_ms: int | None = dc_field( + default=None, metadata=dc_config(field_name="time_budget_ms") + ) # Sort parameters for activity selection sort: "list[SortParamRequest] | None" = dc_field( default=None, metadata=dc_config(field_name="sort") @@ -1365,6 +1401,9 @@ class AddActivityRequest(DataClassJsonMixin): skip_enrich_url: bool | None = dc_field( default=None, metadata=dc_config(field_name="skip_enrich_url") ) + skip_existing: bool | None = dc_field( + default=None, metadata=dc_config(field_name="skip_existing") + ) # Whether to skip push notifications skip_push: bool | None = dc_field( default=None, metadata=dc_config(field_name="skip_push") @@ -1428,6 +1467,10 @@ class AddActivityResponse(DataClassJsonMixin): mention_notifications_created: int | None = dc_field( default=None, metadata=dc_config(field_name="mention_notifications_created") ) + # Contains the activity ID when skip_existing is true and the activity already existed, so it was left unchanged. + skipped_existing: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="skipped_existing") + ) @dataclass @@ -1525,8 +1568,12 @@ class AddCommentReactionRequest(DataClassJsonMixin): @dataclass class AddCommentReactionResponse(DataClassJsonMixin): + # The change this write made to the number of reactions the user holds on this target: 1 when outcome is 'created', 0 when it is 'replaced' or 'unchanged'. These endpoints never return -1; a successful delete-reaction call is what decrements the count. With enforce_unique this is the delta of the user's reaction on the target; without it, the delta of reactions of this type. + counter_delta: int = dc_field(metadata=dc_config(field_name="counter_delta")) # Duration of the request duration: str = dc_field(metadata=dc_config(field_name="duration")) + # What this write did to the user's reaction on this target. One of: created, replaced, unchanged. 'created' means a new reaction was written and nothing was replaced; 'replaced' means enforce_unique removed one or more of the user's other reaction types; 'unchanged' means the user already held this reaction type (its custom data may still have been updated). Without enforce_unique a user can hold several reaction types on one target, so 'created' then means 'this reaction type was newly added', not 'the user's first reaction on this target'. + outcome: str = dc_field(metadata=dc_config(field_name="outcome")) comment: "CommentResponse" = dc_field(metadata=dc_config(field_name="comment")) reaction: "FeedsReactionResponse" = dc_field( metadata=dc_config(field_name="reaction") @@ -1544,6 +1591,10 @@ class AddCommentReactionResponse(DataClassJsonMixin): notification_task_id: str | None = dc_field( default=None, metadata=dc_config(field_name="notification_task_id") ) + # The reaction type this write replaced, or null when nothing was replaced. Non-null exactly when outcome is 'replaced'. If enforce_unique removed several reactions โ€” possible only for data created before enforce_unique was adopted โ€” this is the most recently created one. + previous_reaction_type: str | None = dc_field( + default=None, metadata=dc_config(field_name="previous_reaction_type") + ) reference_activity: "ActivityResponse | None" = dc_field( default=None, metadata=dc_config(field_name="reference_activity") ) @@ -1694,7 +1745,11 @@ class AddReactionRequest(DataClassJsonMixin): @dataclass class AddReactionResponse(DataClassJsonMixin): + # The change this write made to the number of reactions the user holds on this target: 1 when outcome is 'created', 0 when it is 'replaced' or 'unchanged'. These endpoints never return -1; a successful delete-reaction call is what decrements the count. With enforce_unique this is the delta of the user's reaction on the target; without it, the delta of reactions of this type. + counter_delta: int = dc_field(metadata=dc_config(field_name="counter_delta")) duration: str = dc_field(metadata=dc_config(field_name="duration")) + # What this write did to the user's reaction on this target. One of: created, replaced, unchanged. 'created' means a new reaction was written and nothing was replaced; 'replaced' means enforce_unique removed one or more of the user's other reaction types; 'unchanged' means the user already held this reaction type (its custom data may still have been updated). Without enforce_unique a user can hold several reaction types on one target, so 'created' then means 'this reaction type was newly added', not 'the user's first reaction on this target'. + outcome: str = dc_field(metadata=dc_config(field_name="outcome")) activity: "ActivityResponse" = dc_field(metadata=dc_config(field_name="activity")) reaction: "FeedsReactionResponse" = dc_field( metadata=dc_config(field_name="reaction") @@ -1712,6 +1767,10 @@ class AddReactionResponse(DataClassJsonMixin): notification_task_id: str | None = dc_field( default=None, metadata=dc_config(field_name="notification_task_id") ) + # The reaction type this write replaced, or null when nothing was replaced. Non-null exactly when outcome is 'replaced'. If enforce_unique removed several reactions โ€” possible only for data created before enforce_unique was adopted โ€” this is the most recently created one. + previous_reaction_type: str | None = dc_field( + default=None, metadata=dc_config(field_name="previous_reaction_type") + ) reference_activity: "ActivityResponse | None" = dc_field( default=None, metadata=dc_config(field_name="reference_activity") ) @@ -1801,6 +1860,10 @@ class AggregationConfig(DataClassJsonMixin): ) # Format for activity aggregation format: str | None = dc_field(default=None, metadata=dc_config(field_name="format")) + # Maximum number of activities kept in each aggregated group. Omit to use the default: 15 for apps migrated from Feeds v2 or created on or after 2026-10-11, 100 otherwise. Must be between 1 and 100 when set. + group_size: int | None = dc_field( + default=None, metadata=dc_config(field_name="group_size") + ) # Strategy for computing aggregated group scores from member activity scores when ranking is enabled. Valid values: sum, max, avg score_strategy: str | None = dc_field( default=None, metadata=dc_config(field_name="score_strategy") @@ -1849,6 +1912,10 @@ class AnalyzeRequest(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) + # ISO 3166-1 alpha-2 country the content is aimed at (e.g. US, DE). Forwarded to the AI text provider as country context so it can resolve words whose meaning changes between countries. + country_code: str | None = dc_field( + default=None, metadata=dc_config(field_name="country_code") + ) # ID of the user who created the content. Required with entity_type + entity_id; omit all three for stateless mode. entity_creator_id: str | None = dc_field( default=None, metadata=dc_config(field_name="entity_creator_id") @@ -1887,6 +1954,14 @@ class AnalyzeResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) # `complete` (all fields screened), `partial` (mix of verdicts and per-field errors), or `pending` (async). status: str = dc_field(metadata=dc_config(field_name="status")) + # ID of the LLM model that scored the content (e.g. gpt-4o-mini). Omitted when no LLM verdict was used (e.g. Bodyguard-only, or `pending`). + llm_model: str | None = dc_field( + default=None, metadata=dc_config(field_name="llm_model") + ) + # reasoning_effort sent with the LLM request. Omitted when the request did not send one. + llm_reasoning_effort: str | None = dc_field( + default=None, metadata=dc_config(field_name="llm_reasoning_effort") + ) # Per-image moderation verdicts keyed by caller label. images: "dict[str, AnalyzeImageField] | None" = dc_field( default=None, metadata=dc_config(field_name="images") @@ -1919,17 +1994,34 @@ class AnalyzeTextField(DataClassJsonMixin): ) +@dataclass +class AppDefaults(DataClassJsonMixin): + # Defaults applied by the get-or-create channel endpoint. + get_or_create_channel: "GetOrCreateChannelDefaults" = dc_field( + metadata=dc_config(field_name="get_or_create_channel") + ) + # Defaults applied by the query channels endpoint. + query_channels: "QueryChannelsDefaults" = dc_field( + metadata=dc_config(field_name="query_channels") + ) + + @dataclass class AppResponseFields(DataClassJsonMixin): allow_multi_user_devices: bool = dc_field( metadata=dc_config(field_name="allow_multi_user_devices") ) + # A boolean value determining if async url enrichment is enabled. async_url_enrich_enabled: bool = dc_field( metadata=dc_config(field_name="async_url_enrich_enabled") ) + # A boolean value determining if auto translation is enabled. auto_translation_enabled: bool = dc_field( metadata=dc_config(field_name="auto_translation_enabled") ) + before_message_send_hook_system_messages: bool = dc_field( + metadata=dc_config(field_name="before_message_send_hook_system_messages") + ) campaign_enabled: bool = dc_field(metadata=dc_config(field_name="campaign_enabled")) cdn_expiration_seconds: int = dc_field( metadata=dc_config(field_name="cdn_expiration_seconds") @@ -1992,6 +2084,7 @@ class AppResponseFields(DataClassJsonMixin): multi_tenant_enabled: bool = dc_field( metadata=dc_config(field_name="multi_tenant_enabled") ) + # The name of the app. name: str = dc_field(metadata=dc_config(field_name="name")) organization: str = dc_field(metadata=dc_config(field_name="organization")) permission_version: str = dc_field( @@ -2031,6 +2124,8 @@ class AppResponseFields(DataClassJsonMixin): channel_configs: "dict[str, ChannelConfig | None]" = dc_field( metadata=dc_config(field_name="channel_configs") ) + # Request limit defaults applied by the server when the corresponding field is omitted. + defaults: "AppDefaults" = dc_field(metadata=dc_config(field_name="defaults")) file_upload_config: "FileUploadConfig" = dc_field( metadata=dc_config(field_name="file_upload_config") ) @@ -2185,7 +2280,11 @@ class AppealItemResponse(DataClassJsonMixin): ai_text_severity: str | None = dc_field( default=None, metadata=dc_config(field_name="ai_text_severity") ) - # CID of the channel the entity belongs to, if applicable + # Detected language of the appeal_reason text itself + appeal_reason_language: str | None = dc_field( + default=None, metadata=dc_config(field_name="appeal_reason_language") + ) + # CID of the channel the entity belongs to (content appeals), or of the channel ban being appealed (stream:user appeals). Empty for a global ban appeal. channel_cid: str | None = dc_field( default=None, metadata=dc_config(field_name="channel_cid") ) @@ -2229,6 +2328,10 @@ class AppealItemResponse(DataClassJsonMixin): flags: "list[ModerationFlagResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="flags") ) + # Detected languages in the content + languages: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="languages") + ) entity_content: "ModerationPayload | None" = dc_field( default=None, metadata=dc_config(field_name="entity_content") ) @@ -2280,6 +2383,10 @@ class AppealRequest(DataClassJsonMixin): entity_id: str = dc_field(metadata=dc_config(field_name="entity_id")) # Type of entity being appealed (e.g., message, user) entity_type: str = dc_field(metadata=dc_config(field_name="entity_type")) + # CID of the channel ban being appealed. Only used when entity_type is stream:user; omit to appeal the global ban. + channel_cid: str | None = dc_field( + default=None, metadata=dc_config(field_name="channel_cid") + ) # ID of the review queue item (flagged message) that triggered the ban. Applicable only for user ban appeals. review_queue_item_id: str | None = dc_field( default=None, metadata=dc_config(field_name="review_queue_item_id") @@ -2434,6 +2541,49 @@ class AsyncExportErrorEvent(DataClassJsonMixin): ) +@dataclass +class AsyncExportLabelResultsEvent(DataClassJsonMixin): + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + finished_at: datetime = dc_field( + metadata=dc_config( + field_name="finished_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + started_at: datetime = dc_field( + metadata=dc_config( + field_name="started_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + task_id: str = dc_field(metadata=dc_config(field_name="task_id")) + url: str = dc_field(metadata=dc_config(field_name="url")) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + type: str = dc_field( + default="export.label_results.success", metadata=dc_config(field_name="type") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + @dataclass class AsyncExportModerationLogsEvent(DataClassJsonMixin): created_at: datetime = dc_field( @@ -2563,6 +2713,95 @@ class AsyncExportUsersEvent(DataClassJsonMixin): ) +@dataclass +class AsyncImportErrorEvent(DataClassJsonMixin): + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + error: str = dc_field(metadata=dc_config(field_name="error")) + finished_at: datetime = dc_field( + metadata=dc_config( + field_name="finished_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + product: str = dc_field(metadata=dc_config(field_name="product")) + state: str = dc_field(metadata=dc_config(field_name="state")) + task_id: str = dc_field(metadata=dc_config(field_name="task_id")) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + type: str = dc_field(default="import.error", metadata=dc_config(field_name="type")) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + started_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="started_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + +@dataclass +class AsyncImportSuccessEvent(DataClassJsonMixin): + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + finished_at: datetime = dc_field( + metadata=dc_config( + field_name="finished_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + product: str = dc_field(metadata=dc_config(field_name="product")) + state: str = dc_field(metadata=dc_config(field_name="state")) + task_id: str = dc_field(metadata=dc_config(field_name="task_id")) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + type: str = dc_field( + default="import.success", metadata=dc_config(field_name="type") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + started_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="started_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + @dataclass class AsyncModerationCallbackConfig(DataClassJsonMixin): mode: str | None = dc_field(default=None, metadata=dc_config(field_name="mode")) @@ -2948,6 +3187,10 @@ class BanOptions(DataClassJsonMixin): class BanRequest(DataClassJsonMixin): # ID of the user to ban target_user_id: str = dc_field(metadata=dc_config(field_name="target_user_id")) + # Whether to also ban the user from future channels created by the banner + ban_from_future_channels: bool | None = dc_field( + default=None, metadata=dc_config(field_name="ban_from_future_channels") + ) # ID of the user performing the ban banned_by_id: str | None = dc_field( default=None, metadata=dc_config(field_name="banned_by_id") @@ -2959,6 +3202,10 @@ class BanRequest(DataClassJsonMixin): delete_messages: str | None = dc_field( default=None, metadata=dc_config(field_name="delete_messages") ) + # Whether to delete reactions by the banned user on other users' messages + delete_reactions: bool | None = dc_field( + default=None, metadata=dc_config(field_name="delete_reactions") + ) # Whether to ban the user's IP address ip_ban: bool | None = dc_field( default=None, metadata=dc_config(field_name="ip_ban") @@ -3130,7 +3377,7 @@ class BlockListResponse(DataClassJsonMixin): ) # Block list name name: str = dc_field(metadata=dc_config(field_name="name")) - # Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word + # Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word, word_allowlist type: str = dc_field(metadata=dc_config(field_name="type")) # List of words to block words: list[str] = dc_field(metadata=dc_config(field_name="words")) @@ -3671,7 +3918,8 @@ class BulkActionAppealsRequest(DataClassJsonMixin): unban: "UnbanActionRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="unban") ) - # Deprecated: use restore instead โ€” it now also reverses a block or shadow block. Configuration for unblock action. + # Deprecated: Use restore instead, which now also reverses a block or shadow block. Configuration for unblock action. + # Deprecated unblock: "UnblockActionRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="unblock") ) @@ -4751,6 +4999,61 @@ class CallRingEvent(DataClassJsonMixin): user: "UserResponse" = dc_field(metadata=dc_config(field_name="user")) # The type of event: "call.notification" in this case type: str = dc_field(default="call.ring", metadata=dc_config(field_name="type")) + # Identifies this ring of the call session + ring_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="ring_id") + ) + + +@dataclass +class CallRingSettledEvent(DataClassJsonMixin): + call_cid: str = dc_field(metadata=dc_config(field_name="call_cid")) + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # ID of the user who rang + initiated_by: str = dc_field(metadata=dc_config(field_name="initiated_by")) + # Final outcome of the ring: answered_all, answered_some, rejected_all, missed_all, cancelled or mixed + outcome: str = dc_field(metadata=dc_config(field_name="outcome")) + # When the ring was sent + rang_at: datetime = dc_field( + metadata=dc_config( + field_name="rang_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # Identifies this ring of the call session, as carried on call.ring + ring_id: str = dc_field(metadata=dc_config(field_name="ring_id")) + # Call session ID + session_id: str = dc_field(metadata=dc_config(field_name="session_id")) + # Why the ring closed: answered, rejected, cancel, timeout, ended or auto_end + settle_reason: str = dc_field(metadata=dc_config(field_name="settle_reason")) + # When the outcome left ringing + settled_at: datetime = dc_field( + metadata=dc_config( + field_name="settled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # ID of the user whose action closed the ring, or 'server' for the missed-call deadline and automatic ends + settled_by: str = dc_field(metadata=dc_config(field_name="settled_by")) + # Final state of every rung user, keyed by user ID + targets: "dict[str, RingSettledTarget]" = dc_field( + metadata=dc_config(field_name="targets") + ) + # The type of event: "call.ring_settled" in this case + type: str = dc_field( + default="call.ring_settled", metadata=dc_config(field_name="type") + ) @dataclass @@ -5106,6 +5409,9 @@ class CallSettingsRequest(DataClassJsonMixin): ingress: "IngressSettingsRequest | None" = dc_field( default=None, metadata=dc_config(field_name="ingress") ) + lifecycle: "LifecycleSettingsRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="lifecycle") + ) limits: "LimitsSettingsRequest | None" = dc_field( default=None, metadata=dc_config(field_name="limits") ) @@ -5158,6 +5464,10 @@ class CallSettingsResponse(DataClassJsonMixin): individual_recording: "IndividualRecordingSettingsResponse" = dc_field( metadata=dc_config(field_name="individual_recording") ) + # LifecycleSettings is the payload for call lifecycle settings + lifecycle: "LifecycleSettingsResponse" = dc_field( + metadata=dc_config(field_name="lifecycle") + ) limits: "LimitsSettingsResponse" = dc_field(metadata=dc_config(field_name="limits")) raw_recording: "RawRecordingSettingsResponse" = dc_field( metadata=dc_config(field_name="raw_recording") @@ -5998,6 +6308,14 @@ class CancelImportV2TaskResponse(DataClassJsonMixin): @dataclass class CastPollVoteRequest(DataClassJsonMixin): + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) @@ -6027,15 +6345,30 @@ class ChangeFeedVisibilityResponse(DataClassJsonMixin): @dataclass -class ChannelBatchCompletedEvent(DataClassJsonMixin): - batch_created_at: datetime = dc_field( +class ChannelBanResponse(DataClassJsonMixin): + created_at: datetime = dc_field( metadata=dc_config( - field_name="batch_created_at", + field_name="created_at", encoder=encode_datetime, decoder=datetime_from_unix_ns, mm_field=fields.DateTime(format="iso"), ) ) + scopes: list[str] = dc_field(metadata=dc_config(field_name="scopes")) + channel_ban: "ChannelBanScopeResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="channel_ban") + ) + future_channel_ban: "ChannelBanScopeResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="future_channel_ban") + ) + # User response object + user: "UserResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class ChannelBanScopeResponse(DataClassJsonMixin): created_at: datetime = dc_field( metadata=dc_config( field_name="created_at", @@ -6044,28 +6377,69 @@ class ChannelBatchCompletedEvent(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) - finished_at: datetime = dc_field( + shadow: bool = dc_field(metadata=dc_config(field_name="shadow")) + expires: datetime | None = dc_field( + default=None, metadata=dc_config( - field_name="finished_at", + field_name="expires", encoder=encode_datetime, decoder=datetime_from_unix_ns, mm_field=fields.DateTime(format="iso"), - ) - ) - operation: str = dc_field(metadata=dc_config(field_name="operation")) - status: str = dc_field(metadata=dc_config(field_name="status")) - success_channels_count: int = dc_field( - metadata=dc_config(field_name="success_channels_count") - ) - task_id: str = dc_field(metadata=dc_config(field_name="task_id")) - failed_channels: "list[FailedChannelUpdates]" = dc_field( - metadata=dc_config(field_name="failed_channels") + ), ) - custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) - type: str = dc_field( - default="channel_batch_update.completed", metadata=dc_config(field_name="type") + reason: str | None = dc_field(default=None, metadata=dc_config(field_name="reason")) + # User response object + banned_by: "UserResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="banned_by") ) - received_at: datetime | None = dc_field( + + +@dataclass +class ChannelBansScopeTotals(DataClassJsonMixin): + channel: int = dc_field(metadata=dc_config(field_name="channel")) + future_channel: int = dc_field(metadata=dc_config(field_name="future_channel")) + + +@dataclass +class ChannelBatchCompletedEvent(DataClassJsonMixin): + batch_created_at: datetime = dc_field( + metadata=dc_config( + field_name="batch_created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + finished_at: datetime = dc_field( + metadata=dc_config( + field_name="finished_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + operation: str = dc_field(metadata=dc_config(field_name="operation")) + status: str = dc_field(metadata=dc_config(field_name="status")) + success_channels_count: int = dc_field( + metadata=dc_config(field_name="success_channels_count") + ) + task_id: str = dc_field(metadata=dc_config(field_name="task_id")) + failed_channels: "list[FailedChannelUpdates]" = dc_field( + metadata=dc_config(field_name="failed_channels") + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + type: str = dc_field( + default="channel_batch_update.completed", metadata=dc_config(field_name="type") + ) + received_at: datetime | None = dc_field( default=None, metadata=dc_config( field_name="received_at", @@ -6149,6 +6523,10 @@ class ChannelBatchUpdateRequest(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) + # For updateData only. Requires a root cids $eq or $in filter with at most 100 CIDs and no root $or/$and. Split larger selections into requests of at most 100 CIDs. A success_channels_count response means the database update completed; a task_id response means it was queued and must be polled, including on older API nodes. + synchronous: bool | None = dc_field( + default=None, metadata=dc_config(field_name="synchronous") + ) # `updateData` only. Deletes these keys from each channel's existing custom object, leaving every other custom key untouched. Keys are dot-paths; deleting a key that does not exist is a no-op. Cannot be combined with `data.custom` custom_unset: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="custom_unset") @@ -6167,8 +6545,12 @@ class ChannelBatchUpdateRequest(DataClassJsonMixin): @dataclass class ChannelBatchUpdateResponse(DataClassJsonMixin): - # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Positive count of channels selected for a completed synchronous database update, not an affected-row count. Concurrent deletion may reduce the rows written. task_id is absent. + success_channels_count: int | None = dc_field( + default=None, metadata=dc_config(field_name="success_channels_count") + ) + # Present for asynchronous updates. Poll this task even if synchronous was requested: an older API node may have queued the update. task_id: str | None = dc_field( default=None, metadata=dc_config(field_name="task_id") ) @@ -6243,6 +6625,7 @@ class ChannelConfig(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -6393,6 +6776,7 @@ class ChannelConfigWithInfo(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -6852,6 +7236,7 @@ class ChannelMemberResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + user_id: str = dc_field(metadata=dc_config(field_name="user_id")) custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) archived_at: datetime | None = dc_field( default=None, @@ -6935,9 +7320,6 @@ class ChannelMemberResponse(DataClassJsonMixin): # Permission level of the member in the channel (DEPRECATED: use channel_role instead). One of: member, moderator, admin, owner role: str | None = dc_field(default=None, metadata=dc_config(field_name="role")) status: str | None = dc_field(default=None, metadata=dc_config(field_name="status")) - user_id: str | None = dc_field( - default=None, metadata=dc_config(field_name="user_id") - ) deleted_messages: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="deleted_messages") ) @@ -7095,6 +7477,7 @@ class ChannelOwnCapability: QUERY_POLL_VOTES: Final[ChannelOwnCapabilityType] = "query-poll-votes" QUOTE_MESSAGE: Final[ChannelOwnCapabilityType] = "quote-message" READ_EVENTS: Final[ChannelOwnCapabilityType] = "read-events" + SCHEDULE_MESSAGE: Final[ChannelOwnCapabilityType] = "schedule-message" SEARCH_MESSAGES: Final[ChannelOwnCapabilityType] = "search-messages" SEND_CUSTOM_EVENTS: Final[ChannelOwnCapabilityType] = "send-custom-events" SEND_LINKS: Final[ChannelOwnCapabilityType] = "send-links" @@ -7121,9 +7504,11 @@ class ChannelOwnCapability: @dataclass class ChannelPushPreferencesResponse(DataClassJsonMixin): + # The scope level of the push notifications. chat_level: str | None = dc_field( default=None, metadata=dc_config(field_name="chat_level") ) + # If provided the notifications will be disabled until the set date. disabled_until: datetime | None = dc_field( default=None, metadata=dc_config( @@ -7279,21 +7664,30 @@ class ChannelResponse(DataClassJsonMixin): @dataclass class ChannelStateResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) + # List of channel members members: "list[ChannelMemberResponse]" = dc_field( metadata=dc_config(field_name="members") ) + # List of channel messages messages: "list[MessageResponse]" = dc_field( metadata=dc_config(field_name="messages") ) + # List of pinned messages in the channel pinned_messages: "list[MessageResponse]" = dc_field( metadata=dc_config(field_name="pinned_messages") ) threads: "list[ThreadStateResponse]" = dc_field( metadata=dc_config(field_name="threads") ) + # Milliseconds until the current user's slow mode cool down period ends. Only set on client-side get or create channel and get channel requests while the user is under cool down + cooldown_remaining_ms: int | None = dc_field( + default=None, metadata=dc_config(field_name="cooldown_remaining_ms") + ) + # Whether this channel is hidden or not hidden: bool | None = dc_field( default=None, metadata=dc_config(field_name="hidden") ) + # Messages before this date are hidden from the user hide_messages_before: datetime | None = dc_field( default=None, metadata=dc_config( @@ -7303,18 +7697,27 @@ class ChannelStateResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) + # Number of channel watchers watcher_count: int | None = dc_field( default=None, metadata=dc_config(field_name="watcher_count") ) + # Active live locations in the channel active_live_locations: "list[SharedLocationResponseData] | None" = dc_field( default=None, metadata=dc_config(field_name="active_live_locations") ) + # Pending messages that this user has sent pending_messages: "list[PendingMessageResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="pending_messages") ) + # List of read states read: "list[ReadStateResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="read") ) + # The caller's own scheduled, sending and failed messages in this channel. Set only by a client-side GetOrCreateChannel with include_scheduled_messages; absent when not requested or not known, in which case use the scheduled messages query + scheduled_messages: "list[ScheduledMessageResponse] | None" = dc_field( + default=None, metadata=dc_config(field_name="scheduled_messages") + ) + # List of user who is watching the channel watchers: "list[UserResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="watchers") ) @@ -7328,6 +7731,7 @@ class ChannelStateResponse(DataClassJsonMixin): membership: "ChannelMemberResponse | None" = dc_field( default=None, metadata=dc_config(field_name="membership") ) + # The push preference details. push_preferences: "ChannelPushPreferencesResponse | None" = dc_field( default=None, metadata=dc_config(field_name="push_preferences") ) @@ -7350,6 +7754,10 @@ class ChannelStateResponseFields(DataClassJsonMixin): threads: "list[ThreadStateResponse]" = dc_field( metadata=dc_config(field_name="threads") ) + # Milliseconds until the current user's slow mode cool down period ends. Only set on client-side get or create channel and get channel requests while the user is under cool down + cooldown_remaining_ms: int | None = dc_field( + default=None, metadata=dc_config(field_name="cooldown_remaining_ms") + ) # Whether this channel is hidden or not hidden: bool | None = dc_field( default=None, metadata=dc_config(field_name="hidden") @@ -7394,6 +7802,7 @@ class ChannelStateResponseFields(DataClassJsonMixin): membership: "ChannelMemberResponse | None" = dc_field( default=None, metadata=dc_config(field_name="membership") ) + # The push preference details. push_preferences: "ChannelPushPreferencesResponse | None" = dc_field( default=None, metadata=dc_config(field_name="push_preferences") ) @@ -7532,6 +7941,7 @@ class ChannelTypeConfig(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -8220,13 +8630,43 @@ class CheckExternalStorageResponse(DataClassJsonMixin): file_url: str = dc_field(metadata=dc_config(field_name="file_url")) +@dataclass +class CheckGCPPubSubRequest(DataClassJsonMixin): + # Publish the test message with an ordering key through the regional endpoint, as a hook with ordering enabled would; requires gcp_pubsub_region + gcp_pubsub_event_based_ordering_key_enabled: bool | None = dc_field( + default=None, + metadata=dc_config(field_name="gcp_pubsub_event_based_ordering_key_enabled"), + ) + # GCP region used for the regional Pub/Sub endpoint when ordering is enabled + gcp_pubsub_region: str | None = dc_field( + default=None, metadata=dc_config(field_name="gcp_pubsub_region") + ) + # Fully qualified GCP Pub/Sub topic name (projects/{project}/topics/{topic}) + gcp_pubsub_topic: str | None = dc_field( + default=None, metadata=dc_config(field_name="gcp_pubsub_topic") + ) + + +@dataclass +class CheckGCPPubSubResponse(DataClassJsonMixin): + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Validation result. One of: ok, error + status: str = dc_field(metadata=dc_config(field_name="status")) + # Error text + error: str | None = dc_field(default=None, metadata=dc_config(field_name="error")) + # Error data + data: dict[str, object] | None = dc_field( + default=None, metadata=dc_config(field_name="data") + ) + + @dataclass class CheckPushRequest(DataClassJsonMixin): # Push message template for APN apn_template: str | None = dc_field( default=None, metadata=dc_config(field_name="apn_template") ) - # Type of event for push templates (default: message.new). One of: message.new, message.updated, reaction.new, reaction.updated, notification.reminder_due + # Type of event for push templates (default: message.new). One of: message.new, message.updated, reaction.new, reaction.updated, notification.reminder_due, feeds.activity.added, feeds.comment.added, feeds.activity.reaction.added, feeds.comment.reaction.added, feeds.follow.created, feeds.notification_feed.updated event_type: str | None = dc_field( default=None, metadata=dc_config(field_name="event_type") ) @@ -8321,6 +8761,10 @@ class CheckRequest(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) + # ISO 3166-1 alpha-2 country the content is aimed at (e.g. US, DE), used as country context by AI text providers + country_code: str | None = dc_field( + default=None, metadata=dc_config(field_name="country_code") + ) # Whether to run moderation in test mode test_mode: bool | None = dc_field( default=None, metadata=dc_config(field_name="test_mode") @@ -8353,10 +8797,22 @@ class CheckResponse(DataClassJsonMixin): ) # Status of the moderation check (completed or pending) status: str = dc_field(metadata=dc_config(field_name="status")) + # ID of the LLM model that produced the verdict (e.g. gpt-4o-mini). Omitted when no LLM ran synchronously (e.g. Bodyguard-only checks or async moderation) + llm_model: str | None = dc_field( + default=None, metadata=dc_config(field_name="llm_model") + ) + # reasoning_effort sent with the LLM request that produced the verdict. Omitted when the request did not send one + llm_reasoning_effort: str | None = dc_field( + default=None, metadata=dc_config(field_name="llm_reasoning_effort") + ) # ID of the running moderation task task_id: str | None = dc_field( default=None, metadata=dc_config(field_name="task_id") ) + # Score (0-100) of each policy intent topic against this request's text, in policy order (test mode only; omitted when the policy has no topics or scoring failed) + intent_scores: "list[IntentScoreResponse] | None" = dc_field( + default=None, metadata=dc_config(field_name="intent_scores") + ) # All moderation rules triggered by this check (content, user, and call rules), with their resolved actions triggered_rules: "list[TriggeredRuleResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="triggered_rules") @@ -8364,6 +8820,7 @@ class CheckResponse(DataClassJsonMixin): item: "ReviewQueueItemResponse | None" = dc_field( default=None, metadata=dc_config(field_name="item") ) + # Deprecated triggered_rule: "TriggeredRuleResponse | None" = dc_field( default=None, metadata=dc_config(field_name="triggered_rule") ) @@ -8497,6 +8954,14 @@ class ClientEvent(DataClassJsonMixin): join_reason: str | None = dc_field( default=None, metadata=dc_config(field_name="join_reason") ) + # Source of the leave that aborted a join: user, app, callkit, remote_event, sdk_error, or unknown. Not validated against this set, so SDKs may report new values. Optional; sent on failure events. Maximum 32 characters. + leave_initiator: str | None = dc_field( + default=None, metadata=dc_config(field_name="leave_initiator") + ) + # Internal reason or triggering event for the leave that aborted a join. Optional; sent on failure events. Maximum 200 characters. + leave_reason: str | None = dc_field( + default=None, metadata=dc_config(field_name="leave_reason") + ) # Microphone permission status: INITIATED, FAILED, GRANTED, or NOT_INITIATED. Required on every MediaDevicePermission event. microphone_permission_status: str | None = dc_field( default=None, metadata=dc_config(field_name="microphone_permission_status") @@ -8541,6 +9006,8 @@ class ClientEvent(DataClassJsonMixin): ) # Identifier of the SFU the client was attempting to connect to. Required on WSJoin and PeerConnectionConnect failure, and on FirstAudioFrame and FirstVideoFrame. sfu_id: str | None = dc_field(default=None, metadata=dc_config(field_name="sfu_id")) + # Source of the coordinator join. Optional on CoordinatorJoin events; omitted when not provided. + source: str | None = dc_field(default=None, metadata=dc_config(field_name="source")) # Discriminator identifying the event kind. JoinInitiated marks the start of a join attempt; join-lifecycle events use CoordinatorJoin, CoordinatorWS, WSJoin, or PeerConnectionConnect; media-readiness events use FirstAudioFrame or FirstVideoFrame; MediaDevicePermission reports device permission results; other values denote generic client events. stage: str | None = dc_field(default=None, metadata=dc_config(field_name="stage")) # UUID generated by the client at initiation. Identical on the matching completion event. Absent on JoinInitiated. @@ -8801,6 +9268,89 @@ class CommentDeletedEvent(DataClassJsonMixin): ) +@dataclass +class CommentFeedbackEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + comment_feedback: "CommentFeedbackEventPayload" = dc_field( + metadata=dc_config(field_name="comment_feedback") + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + # The type of event: "feeds.comment.feedback" in this case + type: str = dc_field( + default="feeds.comment.feedback", metadata=dc_config(field_name="type") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + user: "UserResponseCommonFields | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class CommentFeedbackEventPayload(DataClassJsonMixin): + # The comment that received feedback + comment_id: str = dc_field(metadata=dc_config(field_name="comment_id")) + # When the feedback was created + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # Whether the comment is hidden for the user + hide: bool = dc_field(metadata=dc_config(field_name="hide")) + # When the feedback was last updated + updated_at: datetime = dc_field( + metadata=dc_config( + field_name="updated_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # User response object + user: "UserResponse" = dc_field(metadata=dc_config(field_name="user")) + + +@dataclass +class CommentFeedbackRequest(DataClassJsonMixin): + # Whether to hide this comment for the user + hide: bool | None = dc_field(default=None, metadata=dc_config(field_name="hide")) + user_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="user_id") + ) + # User request object + user: "UserRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class CommentFeedbackResponse(DataClassJsonMixin): + # The ID of the comment that received feedback + comment_id: str = dc_field(metadata=dc_config(field_name="comment_id")) + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Whether the comment is hidden for the user + hide: bool = dc_field(metadata=dc_config(field_name="hide")) + + @dataclass class CommentReactionAddedEvent(DataClassJsonMixin): # Date/time of creation @@ -8932,6 +9482,8 @@ class CommentResponse(DataClassJsonMixin): ) # Number of downvotes for this comment downvote_count: int = dc_field(metadata=dc_config(field_name="downvote_count")) + # Only true in QueryComments results filtered with hidden: true + hidden: bool = dc_field(metadata=dc_config(field_name="hidden")) # Unique identifier for the comment id: str = dc_field(metadata=dc_config(field_name="id")) # ID of the object this comment is associated with @@ -9128,6 +9680,7 @@ class ConfigOverridesRequest(DataClassJsonMixin): max_message_length: int | None = dc_field( default=None, metadata=dc_config(field_name="max_message_length") ) + # Overrides the push notification level for this channel push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -9234,7 +9787,8 @@ class ConfigResponse(DataClassJsonMixin): metadata=dc_config(field_name="automod_platform_circumvention_config"), ) automod_semantic_filters_config: "AutomodSemanticFiltersConfig | None" = dc_field( - default=None, metadata=dc_config(field_name="automod_semantic_filters_config") + default=None, + metadata=dc_config(field_name="automod_semantic_filters_config"), ) automod_toxicity_config: "AutomodToxicityConfig | None" = dc_field( default=None, metadata=dc_config(field_name="automod_toxicity_config") @@ -9245,6 +9799,9 @@ class ConfigResponse(DataClassJsonMixin): flood_config: "FloodConfig | None" = dc_field( default=None, metadata=dc_config(field_name="flood_config") ) + intent_config: "IntentConfigResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="intent_config") + ) llm_config: "LLMConfig | None" = dc_field( default=None, metadata=dc_config(field_name="llm_config") ) @@ -9266,6 +9823,16 @@ class ContentCountRuleParameters(DataClassJsonMixin): ) +@dataclass +class ContentCountryCodeParameters(DataClassJsonMixin): + operator: str | None = dc_field( + default=None, metadata=dc_config(field_name="operator") + ) + country_codes: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="country_codes") + ) + + @dataclass class ContentCustomPropertyCountParameters(DataClassJsonMixin): operator: str | None = dc_field( @@ -9341,7 +9908,7 @@ class CreateBlockListRequest(DataClassJsonMixin): default=None, metadata=dc_config(field_name="is_substring_matching_enabled") ) team: str | None = dc_field(default=None, metadata=dc_config(field_name="team")) - # Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word + # Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word, word_allowlist type: str | None = dc_field(default=None, metadata=dc_config(field_name="type")) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") @@ -9620,6 +10187,7 @@ class CreateChannelTypeResponse(DataClassJsonMixin): ) custom_events: bool = dc_field(metadata=dc_config(field_name="custom_events")) delivery_events: bool = dc_field(metadata=dc_config(field_name="delivery_events")) + # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) mark_messages_pending: bool = dc_field( metadata=dc_config(field_name="mark_messages_pending") @@ -9677,6 +10245,7 @@ class CreateChannelTypeResponse(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -10070,6 +10639,14 @@ class CreatePolicyTestSetRequest(DataClassJsonMixin): class CreatePollOptionRequest(DataClassJsonMixin): # Option text text: str = dc_field(metadata=dc_config(field_name="text")) + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) @@ -10094,6 +10671,14 @@ class CreatePollRequest(DataClassJsonMixin): allow_user_suggested_options: bool | None = dc_field( default=None, metadata=dc_config(field_name="allow_user_suggested_options") ) + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) # A description of the poll description: str | None = dc_field( default=None, metadata=dc_config(field_name="description") @@ -10111,9 +10696,11 @@ class CreatePollRequest(DataClassJsonMixin): max_votes_allowed: int | None = dc_field( default=None, metadata=dc_config(field_name="max_votes_allowed") ) + team: str | None = dc_field(default=None, metadata=dc_config(field_name="team")) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) + # Represents the visibility of votes in a poll. voting_visibility: str | None = dc_field( default=None, metadata=dc_config(field_name="voting_visibility") ) @@ -10180,6 +10767,15 @@ class CreateQueueRequest(DataClassJsonMixin): @dataclass class CreateReminderRequest(DataClassJsonMixin): + expires_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="expires_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) remind_at: datetime | None = dc_field( default=None, metadata=dc_config( @@ -10831,6 +11427,17 @@ class DeleteFeedsBatchResponse(DataClassJsonMixin): task_id: str = dc_field(metadata=dc_config(field_name="task_id")) +@dataclass +class DeleteFeedsRetentionPolicyRequest(DataClassJsonMixin): + policy: str = dc_field(metadata=dc_config(field_name="policy")) + + +@dataclass +class DeleteFeedsRetentionPolicyResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + + @dataclass class DeleteImportV2TaskResponse(DataClassJsonMixin): # Duration of the request in milliseconds @@ -10879,6 +11486,28 @@ class DeleteModerationTemplateResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) +@dataclass +class DeletePollAnswerRequestPayload(DataClassJsonMixin): + # Reason for deletion + reason: str | None = dc_field(default=None, metadata=dc_config(field_name="reason")) + + +@dataclass +class DeletePollOptionRequestPayload(DataClassJsonMixin): + # Option to delete. Required on an item for a whole poll (create or edit); defaults to the option an add_option / update_option item was raised for + option_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="option_id") + ) + # Reason for deletion + reason: str | None = dc_field(default=None, metadata=dc_config(field_name="reason")) + + +@dataclass +class DeletePollRequestPayload(DataClassJsonMixin): + # Reason for deletion + reason: str | None = dc_field(default=None, metadata=dc_config(field_name="reason")) + + @dataclass class DeleteQueueRequest(DataClassJsonMixin): user_id: str | None = dc_field( @@ -10950,6 +11579,16 @@ class DeleteSIPTrunkResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) +@dataclass +class DeleteScheduledMessageResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse" = dc_field( + metadata=dc_config(field_name="scheduled_message") + ) + + @dataclass class DeleteSegmentTargetsRequest(DataClassJsonMixin): # Target IDs @@ -10962,6 +11601,15 @@ class DeleteTranscriptionResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) +@dataclass +class DeleteUserInterestsResponse(DataClassJsonMixin): + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Interest tags still set on the user + interests: "list[InterestTagResponse]" = dc_field( + metadata=dc_config(field_name="interests") + ) + + @dataclass class DeleteUserMessagesRequestPayload(DataClassJsonMixin): # Message deletion mode: soft, pruning, or hard @@ -11117,7 +11765,7 @@ class DeviceErrorInfo(DataClassJsonMixin): @dataclass class DeviceResponse(DataClassJsonMixin): - # Date/time of creation + # The date when the device was created. created_at: datetime = dc_field( metadata=dc_config( field_name="created_at", @@ -11126,7 +11774,7 @@ class DeviceResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) - # Device ID + # The device identifier. id: str = dc_field(metadata=dc_config(field_name="id")) # Push provider push_provider: str = dc_field(metadata=dc_config(field_name="push_provider")) @@ -11479,6 +12127,10 @@ class EnrichmentOptions(DataClassJsonMixin): include_flat_activities: bool | None = dc_field( default=None, metadata=dc_config(field_name="include_flat_activities") ) + # Default: false. Server-side only. When true, the response includes ranking_config: the ranking configuration (from the feed view, or the feed group as fallback) used to rank this read. + include_ranking_config: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_ranking_config") + ) # Default: false. When true, includes score_vars in activity responses containing variable values used at ranking time. include_score_vars: bool | None = dc_field( default=None, metadata=dc_config(field_name="include_score_vars") @@ -11647,6 +12299,7 @@ class EntityCreatorResponse(DataClassJsonMixin): devices: "list[DeviceResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="devices") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -11705,6 +12358,19 @@ class EventHook(DataClassJsonMixin): enabled: bool | None = dc_field( default=None, metadata=dc_config(field_name="enabled") ) + gcp_pubsub_auth_type: str | None = dc_field( + default=None, metadata=dc_config(field_name="gcp_pubsub_auth_type") + ) + gcp_pubsub_event_based_ordering_key_enabled: bool | None = dc_field( + default=None, + metadata=dc_config(field_name="gcp_pubsub_event_based_ordering_key_enabled"), + ) + gcp_pubsub_region: str | None = dc_field( + default=None, metadata=dc_config(field_name="gcp_pubsub_region") + ) + gcp_pubsub_topic: str | None = dc_field( + default=None, metadata=dc_config(field_name="gcp_pubsub_topic") + ) hook_type: str | None = dc_field( default=None, metadata=dc_config(field_name="hook_type") ) @@ -11740,6 +12406,10 @@ class EventHook(DataClassJsonMixin): sqs_auth_type: str | None = dc_field( default=None, metadata=dc_config(field_name="sqs_auth_type") ) + sqs_event_based_message_group_id_enabled: bool | None = dc_field( + default=None, + metadata=dc_config(field_name="sqs_event_based_message_group_id_enabled"), + ) sqs_key: str | None = dc_field( default=None, metadata=dc_config(field_name="sqs_key") ) @@ -12957,6 +13627,9 @@ class FeedsNotificationComment(DataClassJsonMixin): comment: str = dc_field(metadata=dc_config(field_name="comment")) id: str = dc_field(metadata=dc_config(field_name="id")) user_id: str = dc_field(metadata=dc_config(field_name="user_id")) + parent_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="parent_id") + ) attachments: "list[Attachment] | None" = dc_field( default=None, metadata=dc_config(field_name="attachments") ) @@ -13020,6 +13693,14 @@ class FeedsNotificationTrigger(DataClassJsonMixin): ) +@dataclass +class FeedsPolicyConfig(DataClassJsonMixin): + max_age_hours: int = dc_field(metadata=dc_config(field_name="max_age_hours")) + feed_groups: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="feed_groups") + ) + + @dataclass class FeedsPreferences(DataClassJsonMixin): # Push notification preference for comments on user's activities. One of: all, none @@ -13132,10 +13813,33 @@ class FeedsReactionResponse(DataClassJsonMixin): @dataclass -class FeedsShareResponse(DataClassJsonMixin): - activity_id: str = dc_field(metadata=dc_config(field_name="activity_id")) - created_at: datetime = dc_field( - metadata=dc_config( +class FeedsRetentionPolicy(DataClassJsonMixin): + app_pk: int = dc_field(metadata=dc_config(field_name="app_pk")) + enabled_at: datetime = dc_field( + metadata=dc_config( + field_name="enabled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + policy: str = dc_field(metadata=dc_config(field_name="policy")) + config: "FeedsPolicyConfig" = dc_field(metadata=dc_config(field_name="config")) + + +@dataclass +class FeedsRetentionRunResponse(DataClassJsonMixin): + app_pk: int = dc_field(metadata=dc_config(field_name="app_pk")) + date: str = dc_field(metadata=dc_config(field_name="date")) + policy: str = dc_field(metadata=dc_config(field_name="policy")) + stats: "RunStats" = dc_field(metadata=dc_config(field_name="stats")) + + +@dataclass +class FeedsShareResponse(DataClassJsonMixin): + activity_id: str = dc_field(metadata=dc_config(field_name="activity_id")) + created_at: datetime = dc_field( + metadata=dc_config( field_name="created_at", encoder=encode_datetime, decoder=datetime_from_unix_ns, @@ -13308,6 +14012,7 @@ class FeedsV3CommentResponse(DataClassJsonMixin): ) ) downvote_count: int = dc_field(metadata=dc_config(field_name="downvote_count")) + hidden: bool = dc_field(metadata=dc_config(field_name="hidden")) id: str = dc_field(metadata=dc_config(field_name="id")) object_id: str = dc_field(metadata=dc_config(field_name="object_id")) object_type: str = dc_field(metadata=dc_config(field_name="object_type")) @@ -13430,6 +14135,10 @@ class FilterConfigResponse(DataClassJsonMixin): ai_image_labels: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="ai_image_labels") ) + # Labels the image OCR pipeline can flag, available as filter values on the `label` field under the ai_image category. The app's LLM labels when LLM configurability is enabled, otherwise the AI text labels. OCR and image-classification labels share the `label` field, so a name present in both matches either. + ai_image_ocr_labels: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="ai_image_ocr_labels") + ) # AI text moderation labels available as filter values ai_text_labels: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="ai_text_labels") @@ -13442,6 +14151,10 @@ class FilterConfigResponse(DataClassJsonMixin): filterable_custom_keys: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="filterable_custom_keys") ) + # Names of the app's moderation rules, available as filter values on the `label` field when filtering rule-flagged content. Includes disabled rules, since items flagged before a rule was turned off still carry its name; excludes deleted ones. Scoped to the caller's teams on a multi-tenant app. Capped at 30 names, ordered by name, so an app above that cap is listed partially. + rule_names: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="rule_names") + ) # AI image moderation labels available as filter values, as a map of L1 label to its L2 sub-labels. Reflects the app's effective image taxonomy: custom Bodyguard taxonomy when enabled, otherwise the standard catalogue of the org's enabled image providers. ai_image_taxonomy: "dict[str, list[str]] | None" = dc_field( default=None, metadata=dc_config(field_name="ai_image_taxonomy") @@ -13725,12 +14438,18 @@ class FloodIdenticalConfig(DataClassJsonMixin): @dataclass class FloodIdenticalRuleParameters(DataClassJsonMixin): + min_text_length: int | None = dc_field( + default=None, metadata=dc_config(field_name="min_text_length") + ) threshold: int | None = dc_field( default=None, metadata=dc_config(field_name="threshold") ) time_window: str | None = dc_field( default=None, metadata=dc_config(field_name="time_window") ) + track_across_users: bool | None = dc_field( + default=None, metadata=dc_config(field_name="track_across_users") + ) allowlist: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="allowlist") ) @@ -13755,6 +14474,9 @@ class FloodSimilarConfig(DataClassJsonMixin): @dataclass class FloodSimilarRuleParameters(DataClassJsonMixin): + min_text_length: int | None = dc_field( + default=None, metadata=dc_config(field_name="min_text_length") + ) similarity_distance: int | None = dc_field( default=None, metadata=dc_config(field_name="similarity_distance") ) @@ -13773,6 +14495,8 @@ class FloodSimilarRuleParameters(DataClassJsonMixin): class FollowBatchRequest(DataClassJsonMixin): # List of follow relationships to create follows: "list[FollowRequest]" = dc_field(metadata=dc_config(field_name="follows")) + # If true, the batch is queued as a background task, paced per app, and the response has empty follows and a task_id. Server-side only. Supported by the get-or-create endpoint (GetOrCreateFollows); FollowBatch rejects it. Async batches can apply out of order with other follow writes. Defaults to false. + _async: bool | None = dc_field(default=None, metadata=dc_config(field_name="async")) # If true, auto-creates users referenced by source/target FIDs in the batch when they don't already exist. Server-side only. Defaults to false. This top-level field is the only supported batch/upsert create_users control. create_users: bool | None = dc_field( default=None, metadata=dc_config(field_name="create_users") @@ -13790,6 +14514,10 @@ class FollowBatchResponse(DataClassJsonMixin): created: "list[FollowResponse]" = dc_field(metadata=dc_config(field_name="created")) # List of current follow relationships follows: "list[FollowResponse]" = dc_field(metadata=dc_config(field_name="follows")) + # ID of the background task when the request was sent with async: true + task_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="task_id") + ) @dataclass @@ -14056,6 +14784,8 @@ class FullUserResponse(DataClassJsonMixin): metadata=dc_config(field_name="total_unread_count") ) unread_channels: int = dc_field(metadata=dc_config(field_name="unread_channels")) + # Deprecated: Use total_unread_count instead. + # Deprecated unread_count: int = dc_field(metadata=dc_config(field_name="unread_count")) unread_threads: int = dc_field(metadata=dc_config(field_name="unread_threads")) updated_at: datetime = dc_field( @@ -14132,6 +14862,7 @@ class FullUserResponse(DataClassJsonMixin): latest_hidden_channels: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="latest_hidden_channels") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -14258,6 +14989,7 @@ class GetAppealResponse(DataClassJsonMixin): class GetApplicationResponse(DataClassJsonMixin): # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) + # A type representing the app settings. app: "AppResponseFields" = dc_field(metadata=dc_config(field_name="app")) @@ -14488,6 +15220,7 @@ class GetChannelTypeResponse(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -14554,6 +15287,8 @@ class GetCommentResponse(DataClassJsonMixin): @dataclass class GetCommentsResponse(DataClassJsonMixin): + # Total number of comments on the object, including replies at every depth + comment_count: int = dc_field(metadata=dc_config(field_name="comment_count")) duration: str = dc_field(metadata=dc_config(field_name="duration")) # Sort order used for the comments (first, last, top, best, controversial) sort: str = dc_field(metadata=dc_config(field_name="sort")) @@ -14563,6 +15298,10 @@ class GetCommentsResponse(DataClassJsonMixin): ) next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Number of comments directly on the object, excluding replies. Independent of depth, replies_limit and id_around, and not adjusted per viewer. Only present when include_top_level_comment_count is set + top_level_comment_count: int | None = dc_field( + default=None, metadata=dc_config(field_name="top_level_comment_count") + ) @dataclass @@ -14740,6 +15479,42 @@ class GetFeedsRateLimitsResponse(DataClassJsonMixin): ) +@dataclass +class GetFeedsRetentionPolicyResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + enabled: bool = dc_field(metadata=dc_config(field_name="enabled")) + policies: "list[FeedsRetentionPolicy]" = dc_field( + metadata=dc_config(field_name="policies") + ) + + +@dataclass +class GetFeedsRetentionPolicyRunsRequest(DataClassJsonMixin): + limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Array of sort parameters + sort: "list[SortParamRequest] | None" = dc_field( + default=None, metadata=dc_config(field_name="sort") + ) + # Filter conditions to apply to the query + filter_conditions: dict[str, object] | None = dc_field( + default=None, metadata=dc_config(field_name="filter_conditions") + ) + + +@dataclass +class GetFeedsRetentionPolicyRunsResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + runs: "list[FeedsRetentionRunResponse]" = dc_field( + metadata=dc_config(field_name="runs") + ) + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + + @dataclass class GetFlagCountRequest(DataClassJsonMixin): # ID of the user whose content was flagged @@ -14942,6 +15717,14 @@ class GetOrCreateCallResponse(DataClassJsonMixin): call: "CallResponse" = dc_field(metadata=dc_config(field_name="call")) +@dataclass +class GetOrCreateChannelDefaults(DataClassJsonMixin): + # Resolved defaults for a nested pagination object. + members: "PagerDefaults" = dc_field(metadata=dc_config(field_name="members")) + # Resolved defaults for a nested pagination object. + messages: "PagerDefaults" = dc_field(metadata=dc_config(field_name="messages")) + + @dataclass class GetOrCreateFeedGroupRequest(DataClassJsonMixin): # Role new followers of feeds in this group are given. Either a built-in (feed_follower, feed_member_viewer) or any role your app has defined. Empty means feed_follower. Applied when the follow is accepted, so a follow that starts pending picks it up on approval @@ -15016,6 +15799,10 @@ class GetOrCreateFeedRequest(DataClassJsonMixin): ) view: str | None = dc_field(default=None, metadata=dc_config(field_name="view")) watch: bool | None = dc_field(default=None, metadata=dc_config(field_name="watch")) + # Feature-gated; not available for all apps. Request-scoped list of actor/user IDs whose activities are filtered from this GetOrCreateFeed response only. Merged (union) into the viewing user's in-memory blocked authors for this request; never persisted to blocked_users. Max: 1000. + discard_actors: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="discard_actors") + ) data: "FeedInput | None" = dc_field( default=None, metadata=dc_config(field_name="data") ) @@ -15077,6 +15864,10 @@ class GetOrCreateFeedResponse(DataClassJsonMixin): feed: "FeedResponse" = dc_field(metadata=dc_config(field_name="feed")) next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Activity selectors dropped from this read because they exceeded their time budget (see the time_budget_ms selector setting). Their activities are missing from this page, and they stay out for the rest of this pagination session. Omitted when every selector completed + degraded_selectors: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="degraded_selectors") + ) followers_pagination: "PagerResponse | None" = dc_field( default=None, metadata=dc_config(field_name="followers_pagination") ) @@ -15089,6 +15880,9 @@ class GetOrCreateFeedResponse(DataClassJsonMixin): notification_status: "NotificationStatusResponse | None" = dc_field( default=None, metadata=dc_config(field_name="notification_status") ) + ranking_config: "RankingConfig | None" = dc_field( + default=None, metadata=dc_config(field_name="ranking_config") + ) @dataclass @@ -15341,7 +16135,7 @@ class GetUserGroupResponse(DataClassJsonMixin): @dataclass class GetUserInterestsResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) - # Top-N interest tags sorted by descending count, then alphabetically by tag + # Interest tags sorted by descending weight, then manually set tags before computed ones, then descending count, then alphabetically by tag interests: "list[InterestTagResponse]" = dc_field( metadata=dc_config(field_name="interests") ) @@ -15403,7 +16197,7 @@ class GroupedChannelsBucket(DataClassJsonMixin): next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) # Cursor for the previous page of this group prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) - # Unread channels currently classified into this bucket + # Total unread channels matching this bucket and its filters, independent of page size unread_channels: int | None = dc_field( default=None, metadata=dc_config(field_name="unread_channels") ) @@ -15411,9 +16205,16 @@ class GroupedChannelsBucket(DataClassJsonMixin): @dataclass class GroupedChannelsGroupRequest(DataClassJsonMixin): + # Max channels for this group; omitted or zero uses the request limit limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) + # Cursor for the next page; only allowed when requesting one group next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + # Cursor for the previous page; only allowed when requesting one group prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Optional query_timestamp $gte bound for a stored group, requiring query_timestamp sort; do not repeat its group name. For all, combine group equality/$in and date bounds with $or, or use one standalone group $ne exclusion. An empty object matches a stored group; omitted or null preserves legacy grouping. + filter_conditions: dict[str, object] | None = dc_field( + default=None, metadata=dc_config(field_name="filter_conditions") + ) @dataclass @@ -15423,7 +16224,11 @@ class GroupedQueryChannelsRequest(DataClassJsonMixin): user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) - # Groups to return, keyed by group name. Each group can define limit, next, or prev. 'next' and 'prev' cursors are only allowed when the request contains exactly one group; multi-group pagination is rejected. + # Optional query_timestamp descending with NULLS LAST when enabled. Omitted sort preserves existing last_updated ordering and cursor formats + sort: "list[SortParamRequest] | None" = dc_field( + default=None, metadata=dc_config(field_name="sort") + ) + # Groups to return, keyed by stored group name. Each entry can define limit, next, prev, and optional filter_conditions: date bounds for stored groups; all supports combined group/date branches or one group $ne exclusion. Cursors are only allowed when requesting exactly one group. Omitted or null filters preserve legacy grouping, including all. groups: "dict[str, GroupedChannelsGroupRequest] | None" = dc_field( default=None, metadata=dc_config(field_name="groups") ) @@ -15437,7 +16242,7 @@ class GroupedQueryChannelsRequest(DataClassJsonMixin): class GroupedQueryChannelsResponse(DataClassJsonMixin): # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) - # Predefined channel groups keyed by group name + # Groups keyed by the requested stored group name groups: "dict[str, GroupedChannelsBucket]" = dc_field( metadata=dc_config(field_name="groups") ) @@ -16070,12 +16875,94 @@ class InsertActionLogResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) +@dataclass +class IntentConditionInfo(DataClassJsonMixin): + # Whether the score reached the threshold. + passed: bool = dc_field(metadata=dc_config(field_name="passed")) + # Final (damped) score of the topic in this analysis; 0 when the topic is not defined in the policy. + score: int = dc_field(metadata=dc_config(field_name="score")) + # Minimum score (1-100) the condition requires. + threshold: int = dc_field(metadata=dc_config(field_name="threshold")) + # Policy intent topic label the condition references. + topic: str = dc_field(metadata=dc_config(field_name="topic")) + + +@dataclass +class IntentConfigRequest(DataClassJsonMixin): + # Topics to classify conversation text against (max 20, labels must be unique) + topics: "list[IntentTopicRequest] | None" = dc_field( + default=None, metadata=dc_config(field_name="topics") + ) + + +@dataclass +class IntentConfigResponse(DataClassJsonMixin): + # Topics conversation text is classified against + topics: "list[IntentTopicResponse]" = dc_field( + metadata=dc_config(field_name="topics") + ) + + +@dataclass +class IntentEvaluationInfo(DataClassJsonMixin): + # The analyzed conversation: the channel CID for chat, the object ID for feeds. + conversation_id: str = dc_field(metadata=dc_config(field_name="conversation_id")) + # Number of conversation messages sent to the model. + message_count: int = dc_field(metadata=dc_config(field_name="message_count")) + # One entry per `intent_rule` condition of the rule, flat conditions first, then group conditions. + conditions: "list[IntentConditionInfo]" = dc_field( + metadata=dc_config(field_name="conditions") + ) + # Score (0-100) the model gave every topic in the analysis call, keyed by topic label, before message-count damping. + raw_scores: "dict[str, int]" = dc_field(metadata=dc_config(field_name="raw_scores")) + # Final score (0-100) of every topic after message-count damping, keyed by topic label. Conditions compare against these. + scores: "dict[str, int]" = dc_field(metadata=dc_config(field_name="scores")) + + +@dataclass +class IntentRuleParameters(DataClassJsonMixin): + topic: str = dc_field(metadata=dc_config(field_name="topic")) + threshold: int | None = dc_field( + default=None, metadata=dc_config(field_name="threshold") + ) + + +@dataclass +class IntentScoreResponse(DataClassJsonMixin): + score: int = dc_field(metadata=dc_config(field_name="score")) + topic: str = dc_field(metadata=dc_config(field_name="topic")) + + +@dataclass +class IntentTopicRequest(DataClassJsonMixin): + # Unique topic label (e.g. purchase_intent) + label: str = dc_field(metadata=dc_config(field_name="label")) + # Optional description used in the classification prompt; the label alone is used when empty + description: str | None = dc_field( + default=None, metadata=dc_config(field_name="description") + ) + + +@dataclass +class IntentTopicResponse(DataClassJsonMixin): + # Unique topic label (e.g. purchase_intent) + label: str = dc_field(metadata=dc_config(field_name="label")) + # Optional description used in the classification prompt; the label alone is used when empty + description: str | None = dc_field( + default=None, metadata=dc_config(field_name="description") + ) + + @dataclass class InterestTagResponse(DataClassJsonMixin): - # Number of distinct reacted-to activities tagged with this value + # Lifetime number of distinct reacted-to activities tagged with this value, without decay; 0 for manually set tags count: int = dc_field(metadata=dc_config(field_name="count")) + # How the tag was set: computed (from the user's reactions) or manual (through the API) + source: str = dc_field(metadata=dc_config(field_name="source")) # The interest tag value tag: str = dc_field(metadata=dc_config(field_name="tag")) + # Ranking weight between -1.0 and 1.0. Computed tags carry a recency-decayed weight in (0, 1.0]: the user's strongest tag is 1.0 and every other a proportional share + weight: float = dc_field(metadata=dc_config(field_name="weight")) @dataclass @@ -16299,7 +17186,7 @@ class LabelsRequest(DataClassJsonMixin): dry_run: bool | None = dc_field( default=None, metadata=dc_config(field_name="dry_run") ) - # Optional moderation policy key (max 128 chars). For username moderation, set this to a policy whose key starts with 'username:' (e.g. 'username:default') to opt into the low-latency fast-path: blocklists (customer + Stream-managed defaults) short-circuit the LLM, and the LLM fallback uses gpt-4.1-nano with a 24h Valkey verdict cache. Without a 'username:' prefix the request falls through to the standard Bodyguard Analyze v1 username path. + # Optional moderation policy key (max 128 chars). For username moderation, set this to a policy whose key starts with 'username:' (e.g. 'username:default') to opt into the low-latency fast-path: blocklists (customer + Stream-managed defaults) short-circuit the LLM, and the LLM fallback uses gpt-4o-mini (overridable via moderation_settings.llm_username_model) with a 24h Valkey verdict cache. Without a 'username:' prefix the request falls through to the standard Bodyguard Analyze v1 username path. policy: str | None = dc_field(default=None, metadata=dc_config(field_name="policy")) # Optional customer-supplied user identifier for the content author (max 256 chars). Enables filtering stored results by user_id. user_id: str | None = dc_field( @@ -16391,6 +17278,18 @@ class LayoutSettingsResponse(DataClassJsonMixin): ) +@dataclass +class LifecycleSettingsRequest(DataClassJsonMixin): + # Call lifecycle mode. One of: reusable, single_use. A single_use call cannot be reused once it has ended; the server ends it when its ring settles without an answer (currently: when the caller cancels the ring). Fixed at call creation + mode: str | None = dc_field(default=None, metadata=dc_config(field_name="mode")) + + +@dataclass +class LifecycleSettingsResponse(DataClassJsonMixin): + # Call lifecycle mode. One of: reusable, single_use. A single_use call cannot be reused once it has ended; the server ends it when its ring settles without an answer (currently: when the caller cancels the ring). Fixed at call creation + mode: str = dc_field(metadata=dc_config(field_name="mode")) + + @dataclass class LimitInfoResponse(DataClassJsonMixin): # The maximum number of API calls allowed per time window @@ -17109,6 +18008,7 @@ class MemberUserRequest(DataClassJsonMixin): custom: dict[str, object] | None = dc_field( default=None, metadata=dc_config(field_name="custom") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -17477,6 +18377,7 @@ class MessageModerationResult(DataClassJsonMixin): moderated_by: str | None = dc_field( default=None, metadata=dc_config(field_name="moderated_by") ) + # Response from AI moderation ai_moderation_response: "ModerationResponse | None" = dc_field( default=None, metadata=dc_config(field_name="ai_moderation_response") ) @@ -18651,7 +19552,8 @@ class ModerationConfig(DataClassJsonMixin): metadata=dc_config(field_name="automod_platform_circumvention_config"), ) automod_semantic_filters_config: "AutomodSemanticFiltersConfig | None" = dc_field( - default=None, metadata=dc_config(field_name="automod_semantic_filters_config") + default=None, + metadata=dc_config(field_name="automod_semantic_filters_config"), ) automod_toxicity_config: "AutomodToxicityConfig | None" = dc_field( default=None, metadata=dc_config(field_name="automod_toxicity_config") @@ -18725,6 +19627,9 @@ class ModerationDashboardPreferences(DataClassJsonMixin): custom_views_enabled: bool | None = dc_field( default=None, metadata=dc_config(field_name="custom_views_enabled") ) + disable_action_logs: bool | None = dc_field( + default=None, metadata=dc_config(field_name="disable_action_logs") + ) disable_audit_logs: bool | None = dc_field( default=None, metadata=dc_config(field_name="disable_audit_logs") ) @@ -18941,6 +19846,9 @@ class ModerationMarkReviewedEvent(DataClassJsonMixin): @dataclass class ModerationPayload(DataClassJsonMixin): + country_code: str | None = dc_field( + default=None, metadata=dc_config(field_name="country_code") + ) audios: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="audios") ) @@ -19071,9 +19979,13 @@ class ModerationQueueResponse(DataClassJsonMixin): @dataclass class ModerationResponse(DataClassJsonMixin): + # Action taken by automod action: str = dc_field(metadata=dc_config(field_name="action")) + # Explicit score explicit: float = dc_field(metadata=dc_config(field_name="explicit")) + # Spam score spam: float = dc_field(metadata=dc_config(field_name="spam")) + # Toxicity score toxic: float = dc_field(metadata=dc_config(field_name="toxic")) @@ -19174,6 +20086,9 @@ class ModerationRulesTriggeredEvent(DataClassJsonMixin): matched_contents: "list[MatchedContent] | None" = dc_field( default=None, metadata=dc_config(field_name="matched_contents") ) + intent: "IntentEvaluationInfo | None" = dc_field( + default=None, metadata=dc_config(field_name="intent") + ) @dataclass @@ -19374,9 +20289,17 @@ class NoiseCancellationSettings(DataClassJsonMixin): @dataclass class NotificationComment(DataClassJsonMixin): + # The text of the comment comment: str = dc_field(metadata=dc_config(field_name="comment")) + # The ID of the comment id: str = dc_field(metadata=dc_config(field_name="id")) + # The ID of the user who wrote the comment user_id: str = dc_field(metadata=dc_config(field_name="user_id")) + # The ID of the parent comment when this comment is a reply; omitted for top-level comments + parent_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="parent_id") + ) + # Attachments on the comment attachments: "list[Attachment] | None" = dc_field( default=None, metadata=dc_config(field_name="attachments") ) @@ -19905,6 +20828,8 @@ class OwnUserResponse(DataClassJsonMixin): metadata=dc_config(field_name="total_unread_count") ) unread_channels: int = dc_field(metadata=dc_config(field_name="unread_channels")) + # Deprecated: Use total_unread_count instead. + # Deprecated unread_count: int = dc_field(metadata=dc_config(field_name="unread_count")) unread_threads: int = dc_field(metadata=dc_config(field_name="unread_threads")) updated_at: datetime = dc_field( @@ -19915,6 +20840,9 @@ class OwnUserResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + blocked_user_ids: list[str] = dc_field( + metadata=dc_config(field_name="blocked_user_ids") + ) channel_mutes: "list[ChannelMute]" = dc_field( metadata=dc_config(field_name="channel_mutes") ) @@ -19963,15 +20891,14 @@ class OwnUserResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) - blocked_user_ids: list[str] | None = dc_field( - default=None, metadata=dc_config(field_name="blocked_user_ids") - ) latest_hidden_channels: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="latest_hidden_channels") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) + # The push preference details. push_preferences: "PushPreferencesResponse | None" = dc_field( default=None, metadata=dc_config(field_name="push_preferences") ) @@ -19983,6 +20910,12 @@ class OwnUserResponse(DataClassJsonMixin): ) +@dataclass +class PagerDefaults(DataClassJsonMixin): + # Number of records returned when `limit` is omitted. + limit: int = dc_field(metadata=dc_config(field_name="limit")) + + @dataclass class PagerRequest(DataClassJsonMixin): limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) @@ -20700,6 +21633,76 @@ class PolicyTestTotals(DataClassJsonMixin): unscored: int = dc_field(metadata=dc_config(field_name="unscored")) +@dataclass +class PollClosedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + # The type of event: "poll.closed" in this case + type: str = dc_field(default="poll.closed", metadata=dc_config(field_name="type")) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + +@dataclass +class PollDeletedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + # The type of event: "poll.deleted" in this case + type: str = dc_field(default="poll.deleted", metadata=dc_config(field_name="type")) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + @dataclass class PollOptionInput(DataClassJsonMixin): text: str | None = dc_field(default=None, metadata=dc_config(field_name="text")) @@ -20731,6 +21734,9 @@ class PollOptionResponseData(DataClassJsonMixin): id: str = dc_field(metadata=dc_config(field_name="id")) text: str = dc_field(metadata=dc_config(field_name="text")) custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + text_i18n: "dict[str, str] | None" = dc_field( + default=None, metadata=dc_config(field_name="text_i18n") + ) @dataclass @@ -20801,22 +21807,17 @@ class PollResponseData(DataClassJsonMixin): created_by: "UserResponse | None" = dc_field( default=None, metadata=dc_config(field_name="created_by") ) - - -@dataclass -class PollVoteResponse(DataClassJsonMixin): - # Duration of the request in milliseconds - duration: str = dc_field(metadata=dc_config(field_name="duration")) - poll: "PollResponseData | None" = dc_field( - default=None, metadata=dc_config(field_name="poll") + description_i18n: "dict[str, str] | None" = dc_field( + default=None, metadata=dc_config(field_name="description_i18n") ) - vote: "PollVoteResponseData | None" = dc_field( - default=None, metadata=dc_config(field_name="vote") + name_i18n: "dict[str, str] | None" = dc_field( + default=None, metadata=dc_config(field_name="name_i18n") ) @dataclass -class PollVoteResponseData(DataClassJsonMixin): +class PollUpdatedEvent(DataClassJsonMixin): + # Date/time of creation created_at: datetime = dc_field( metadata=dc_config( field_name="created_at", @@ -20825,35 +21826,204 @@ class PollVoteResponseData(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) - id: str = dc_field(metadata=dc_config(field_name="id")) - option_id: str = dc_field(metadata=dc_config(field_name="option_id")) - poll_id: str = dc_field(metadata=dc_config(field_name="poll_id")) - updated_at: datetime = dc_field( + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + # The type of event: "poll.updated" in this case + type: str = dc_field(default="poll.updated", metadata=dc_config(field_name="type")) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, metadata=dc_config( - field_name="updated_at", + field_name="received_at", encoder=encode_datetime, decoder=datetime_from_unix_ns, mm_field=fields.DateTime(format="iso"), - ) - ) - answer_text: str | None = dc_field( - default=None, metadata=dc_config(field_name="answer_text") - ) - is_answer: bool | None = dc_field( - default=None, metadata=dc_config(field_name="is_answer") - ) - user_id: str | None = dc_field( - default=None, metadata=dc_config(field_name="user_id") - ) - # User response object - user: "UserResponse | None" = dc_field( - default=None, metadata=dc_config(field_name="user") + ), ) @dataclass -class PollVotesResponse(DataClassJsonMixin): - # Duration of the request in milliseconds +class PollVoteCastedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + poll_vote: "PollVoteResponseData" = dc_field( + metadata=dc_config(field_name="poll_vote") + ) + # The type of event: "poll.vote_casted" in this case + type: str = dc_field( + default="poll.vote_casted", metadata=dc_config(field_name="type") + ) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + +@dataclass +class PollVoteChangedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + poll_vote: "PollVoteResponseData" = dc_field( + metadata=dc_config(field_name="poll_vote") + ) + # The type of event: "poll.vote_changed" in this case + type: str = dc_field( + default="poll.vote_changed", metadata=dc_config(field_name="type") + ) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + +@dataclass +class PollVoteRemovedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + poll: "PollResponseData" = dc_field(metadata=dc_config(field_name="poll")) + poll_vote: "PollVoteResponseData" = dc_field( + metadata=dc_config(field_name="poll_vote") + ) + # The type of event: "poll.vote_removed" in this case + type: str = dc_field( + default="poll.vote_removed", metadata=dc_config(field_name="type") + ) + activity_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="activity_id") + ) + # The CID of the channel containing the poll + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # The ID of the message containing the poll + message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="message_id") + ) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + +@dataclass +class PollVoteResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + poll: "PollResponseData | None" = dc_field( + default=None, metadata=dc_config(field_name="poll") + ) + vote: "PollVoteResponseData | None" = dc_field( + default=None, metadata=dc_config(field_name="vote") + ) + + +@dataclass +class PollVoteResponseData(DataClassJsonMixin): + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + id: str = dc_field(metadata=dc_config(field_name="id")) + option_id: str = dc_field(metadata=dc_config(field_name="option_id")) + poll_id: str = dc_field(metadata=dc_config(field_name="poll_id")) + updated_at: datetime = dc_field( + metadata=dc_config( + field_name="updated_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + answer_text: str | None = dc_field( + default=None, metadata=dc_config(field_name="answer_text") + ) + is_answer: bool | None = dc_field( + default=None, metadata=dc_config(field_name="is_answer") + ) + user_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="user_id") + ) + answer_text_i18n: "dict[str, str] | None" = dc_field( + default=None, metadata=dc_config(field_name="answer_text_i18n") + ) + # User response object + user: "UserResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class PollVotesResponse(DataClassJsonMixin): + # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) # Poll votes votes: "list[PollVoteResponseData]" = dc_field( @@ -20941,12 +22111,15 @@ class PredefinedFilterStatsResponse(DataClassJsonMixin): @dataclass class PrivacySettingsResponse(DataClassJsonMixin): + # The settings for the delivery receipt events. delivery_receipts: "DeliveryReceiptsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="delivery_receipts") ) + # The settings for the read receipt events. read_receipts: "ReadReceiptsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="read_receipts") ) + # The settings for typing indicator events. typing_indicators: "TypingIndicatorsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="typing_indicators") ) @@ -21168,9 +22341,11 @@ class PushPreferencesResponse(DataClassJsonMixin): call_level: str | None = dc_field( default=None, metadata=dc_config(field_name="call_level") ) + # The scope level of the push notifications. chat_level: str | None = dc_field( default=None, metadata=dc_config(field_name="chat_level") ) + # If provided the notifications will be disabled until the set date. disabled_until: datetime | None = dc_field( default=None, metadata=dc_config( @@ -21582,10 +22757,14 @@ class QueryActivitiesRequest(DataClassJsonMixin): include_expired_activities: bool | None = dc_field( default=None, metadata=dc_config(field_name="include_expired_activities") ) + # When true, include activities hidden by moderation (shadowed or removed). Server-side only; not allowed when a user is set. + include_moderated_activities: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_moderated_activities") + ) include_private_activities: bool | None = dc_field( default=None, metadata=dc_config(field_name="include_private_activities") ) - # When true, include soft-deleted activities in the result. + # When true, include soft-deleted activities in the result. Server-side only; not allowed when a user is set. include_soft_deleted_activities: bool | None = dc_field( default=None, metadata=dc_config(field_name="include_soft_deleted_activities") ) @@ -21937,6 +23116,38 @@ class QueryCallParticipantsResponse(DataClassJsonMixin): call: "CallResponse" = dc_field(metadata=dc_config(field_name="call")) +@dataclass +class QueryCallRingAttemptsRequest(DataClassJsonMixin): + # Call ID + id: str = dc_field(metadata=dc_config(field_name="id")) + # The type of call + type: str = dc_field(metadata=dc_config(field_name="type")) + # Limit the attempts to one call session; omit to read every session of the call + call_session_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="call_session_id") + ) + + +@dataclass +class QueryCallRingAttemptsResponse(DataClassJsonMixin): + # The ID of the call the attempts belong to + call_id: str = dc_field(metadata=dc_config(field_name="call_id")) + # The type of the call the attempts belong to + call_type: str = dc_field(metadata=dc_config(field_name="call_type")) + # The number of attempts returned + count: int = dc_field(metadata=dc_config(field_name="count")) + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # The attempts, newest first + ring_attempts: "list[RingAttemptResponse]" = dc_field( + metadata=dc_config(field_name="ring_attempts") + ) + # The call session the attempts were limited to, null when every session was read + call_session_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="call_session_id") + ) + + @dataclass class QueryCallSessionParticipantStatsResponse(DataClassJsonMixin): call_id: str = dc_field(metadata=dc_config(field_name="call_id")) @@ -22167,6 +23378,59 @@ class QueryCampaignsResponse(DataClassJsonMixin): prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) +@dataclass +class QueryChannelBansRequest(DataClassJsonMixin): + # Whether to exclude expired bans or not + exclude_expired_bans: bool | None = dc_field( + default=None, metadata=dc_config(field_name="exclude_expired_bans") + ) + # When true, the response includes total and total_by_scope: the number of banned users overall and per scope, independent of paging + include_total: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_total") + ) + limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + user_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="user_id") + ) + # Filter conditions to apply to the query + filter_conditions: dict[str, object] | None = dc_field( + default=None, metadata=dc_config(field_name="filter_conditions") + ) + # User request object + user: "UserRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class QueryChannelBansResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Users banned from the channel, one entry per user, newest ban first + bans: "list[ChannelBanResponse]" = dc_field(metadata=dc_config(field_name="bans")) + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Number of banned users, each counted once, ignoring paging. Counted per scope with a cap of 100000 each, so it can reach 200000. Only present when include_total is set on the request; omitted when counting timed out + total: int | None = dc_field(default=None, metadata=dc_config(field_name="total")) + total_by_scope: "ChannelBansScopeTotals | None" = dc_field( + default=None, metadata=dc_config(field_name="total_by_scope") + ) + + +@dataclass +class QueryChannelsDefaults(DataClassJsonMixin): + # Number of channels returned when `limit` is omitted. + limit: int = dc_field(metadata=dc_config(field_name="limit")) + # Largest number of channels returned for one request. A bigger `limit` is silently clamped to this value rather than rejected. An app with no per-app override gets a higher ceiling for the narrow case of a request asking for at most one message and one member per channel. + max_limit: int = dc_field(metadata=dc_config(field_name="max_limit")) + # Number of members returned per channel when `member_limit` is omitted. + member_limit: int = dc_field(metadata=dc_config(field_name="member_limit")) + # Number of messages returned per channel when `message_limit` is omitted. + message_limit: int = dc_field(metadata=dc_config(field_name="message_limit")) + + @dataclass class QueryChannelsRequest(DataClassJsonMixin): # Number of channels to limit @@ -22296,6 +23560,10 @@ class QueryCommentsRequest(DataClassJsonMixin): id_around: str | None = dc_field( default=None, metadata=dc_config(field_name="id_around") ) + # When true, include soft-deleted comments in the result. Server-side only, and requires an object_id filter. Covers comments removed via deleteComment and via soft user-data deletion. Moderation-actioned comments (removed, hidden, shadow_blocked) remain excluded, as are comments whose parent activity is itself deleted. + include_soft_deleted_comments: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_soft_deleted_comments") + ) # Maximum number of comments to return limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) @@ -22482,6 +23750,10 @@ class QueryFeedsUsageStatsResponse(DataClassJsonMixin): @dataclass class QueryFollowsRequest(DataClassJsonMixin): + # When true (server-side only), the response includes group_counts: accepted following counts keyed by the followed feed's group. Requires exactly one source_feed / source_fid string equality. HTTP 400 if target_feed appears, if two source FIDs are given, or if $in / $or / $nor mentions those keys. Independent of paging and of other filters, including status. + include_group_counts: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_group_counts") + ) limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) @@ -22504,6 +23776,10 @@ class QueryFollowsResponse(DataClassJsonMixin): next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) # Cursor for previous page prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + # Accepted following counts keyed by followed feed group. Present only when include_group_counts is set with a source_feed equality. + group_counts: "dict[str, int] | None" = dc_field( + default=None, metadata=dc_config(field_name="group_counts") + ) @dataclass @@ -22930,9 +24206,24 @@ class QueryPinnedActivitiesRequest(DataClassJsonMixin): enrich_own_fields: bool | None = dc_field( default=None, metadata=dc_config(field_name="enrich_own_fields") ) + # When true, return pinned activities even if they have expired. Server-side only; not allowed when a user is set. + include_expired_activities: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_expired_activities") + ) + # When true, return pinned activities even if moderation hides them (shadowed or removed). Server-side only; not allowed when a user is set. + include_moderated_activities: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_moderated_activities") + ) + # When true, return pinned activities even if they are soft-deleted. Server-side only; not allowed when a user is set. + include_soft_deleted_activities: bool | None = dc_field( + default=None, metadata=dc_config(field_name="include_soft_deleted_activities") + ) limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) + user_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="user_id") + ) # Sorting parameters for the query sort: "list[SortParamRequest] | None" = dc_field( default=None, metadata=dc_config(field_name="sort") @@ -22941,6 +24232,10 @@ class QueryPinnedActivitiesRequest(DataClassJsonMixin): filter: dict[str, object] | None = dc_field( default=None, metadata=dc_config(field_name="filter") ) + # User request object + user: "UserRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) @dataclass @@ -23106,7 +24401,7 @@ class QueryReviewQueueRequest(DataClassJsonMixin): sort: "list[SortParamRequest] | None" = dc_field( default=None, metadata=dc_config(field_name="sort") ) - # Filter conditions for review queue items. Accepts built-in fields (e.g. status, channel_cid, severity, recommended_action) and customer-supplied moderation_payload.custom keys: any key that is not a built-in field is matched against the item's custom moderation data (e.g. {"location_id": "loc-42"}). Use filter_config.filterable_custom_keys to discover which custom keys the app exposes as chips. + # Filter conditions for review queue items. Accepts built-in fields (e.g. status, channel_cid, severity, recommended_action) and customer-supplied moderation_payload.custom keys: any key that is not a built-in field is matched against the item's custom moderation data (e.g. {"location_id": "loc-42"}). Use filter_config.filterable_custom_keys to discover which custom keys the app exposes as chips. content_text searches the moderated text: "$q" is a keyword search (terms ANDed, no adjacency) over the indexed tsvector, while "$eq" (or "$in" for several wordings) matches the text exactly. The exact form is unindexed, so scope it with date_range rather than running it across the whole queue. filter: dict[str, object] | None = dc_field( default=None, metadata=dc_config(field_name="filter") ) @@ -23164,6 +24459,41 @@ class QueryRevisionHistoryResponse(DataClassJsonMixin): prev: str | None = dc_field(default=None, metadata=dc_config(field_name="prev")) +@dataclass +class QueryScheduledMessagesRequest(DataClassJsonMixin): + # Page size, 1 to 100. Defaults to 20 + limit: int | None = dc_field(default=None, metadata=dc_config(field_name="limit")) + # Cursor from the previous page + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + user_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="user_id") + ) + # Only scheduled_at ascending is supported. Pending messages come before failed ones + sort: "list[SortParamRequest] | None" = dc_field( + default=None, metadata=dc_config(field_name="sort") + ) + # Filter for querying scheduled messages + filter: "ScheduledMessagesFilter | None" = dc_field( + default=None, metadata=dc_config(field_name="filter") + ) + # User request object + user: "UserRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="user") + ) + + +@dataclass +class QueryScheduledMessagesResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # Pending messages first, then failed ones, each by scheduled_at + scheduled_messages: "list[ScheduledMessageResponse]" = dc_field( + metadata=dc_config(field_name="scheduled_messages") + ) + # Cursor for the next page; empty on the last page + next: str | None = dc_field(default=None, metadata=dc_config(field_name="next")) + + @dataclass class QuerySegmentTargetsRequest(DataClassJsonMixin): # Limit @@ -23437,6 +24767,10 @@ class RTMPSettingsResponse(DataClassJsonMixin): class RankingConfig(DataClassJsonMixin): # Type of ranking algorithm. Required. One of: expression, interest type: str = dc_field(metadata=dc_config(field_name="type")) + # Activities scoring strictly below this value are dropped from the ranked feed. Not supported on aggregated feeds + exclude_below: float | None = dc_field( + default=None, metadata=dc_config(field_name="exclude_below") + ) # Scoring formula. Required when type is 'expression' or 'interest' score: str | None = dc_field(default=None, metadata=dc_config(field_name="score")) # Default values for ranking @@ -24163,10 +25497,19 @@ class ReminderResponseData(DataClassJsonMixin): ) ) user_id: str = dc_field(metadata=dc_config(field_name="user_id")) - remind_at: datetime | None = dc_field( + expires_at: datetime | None = dc_field( default=None, metadata=dc_config( - field_name="remind_at", + field_name="expires_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + remind_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="remind_at", encoder=encode_datetime, decoder=datetime_from_unix_ns, mm_field=fields.DateTime(format="iso"), @@ -24581,6 +25924,9 @@ class ReviewQueueItemResponse(DataClassJsonMixin): config_key: str | None = dc_field( default=None, metadata=dc_config(field_name="config_key") ) + country_code: str | None = dc_field( + default=None, metadata=dc_config(field_name="country_code") + ) # ID of who created the entity entity_creator_id: str | None = dc_field( default=None, metadata=dc_config(field_name="entity_creator_id") @@ -24651,6 +25997,9 @@ class ReviewQueueItemResponse(DataClassJsonMixin): moderation_payload: "ModerationPayloadResponse | None" = dc_field( default=None, metadata=dc_config(field_name="moderation_payload") ) + poll: "PollResponseData | None" = dc_field( + default=None, metadata=dc_config(field_name="poll") + ) reaction: "Reaction | None" = dc_field( default=None, metadata=dc_config(field_name="reaction") ) @@ -24714,6 +26063,72 @@ class RevisionHistoryResponse(DataClassJsonMixin): ) +@dataclass +class RingAttemptResponse(DataClassJsonMixin): + # The application this ring belongs to + app_pk: int = dc_field(metadata=dc_config(field_name="app_pk")) + # The ID of the call that was rung + call_id: str = dc_field(metadata=dc_config(field_name="call_id")) + # The type of the call that was rung + call_type: str = dc_field(metadata=dc_config(field_name="call_type")) + # When the ring went out + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # When the ring expires, i.e. when the missed call deadline fires + expires_at: datetime = dc_field( + metadata=dc_config( + field_name="expires_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # The user that started the ring + initiated_by: str = dc_field(metadata=dc_config(field_name="initiated_by")) + # The derived outcome: ringing, answered_some, answered_all, rejected_all, missed_all, cancelled or mixed + outcome: str = dc_field(metadata=dc_config(field_name="outcome")) + # The ring, as published in the ring_id of the call.ring event + ring_id: str = dc_field(metadata=dc_config(field_name="ring_id")) + # The call session that was rung + session_id: str = dc_field(metadata=dc_config(field_name="session_id")) + # Why the ring settled: answered, rejected, cancel, timeout, ended or auto_end; empty while it is still ringing + settle_reason: str = dc_field(metadata=dc_config(field_name="settle_reason")) + # The user that settled the ring, or 'server'; empty while it is still ringing + settled_by: str = dc_field(metadata=dc_config(field_name="settled_by")) + # Opaque context stored on the ring by the caller + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + # The rung users, keyed by user ID + targets: "dict[str, RingTargetResponse]" = dc_field( + metadata=dc_config(field_name="targets") + ) + # When the ring was deleted + deleted_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="deleted_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # When the outcome left ringing + settled_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="settled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + + @dataclass class RingCallRequest(DataClassJsonMixin): # Indicate if call should be video @@ -24722,6 +26137,10 @@ class RingCallRequest(DataClassJsonMixin): members_ids: list[str] | None = dc_field( default=None, metadata=dc_config(field_name="members_ids") ) + # Opaque context stored on the ring attempt; refs and IDs only + custom: dict[str, object] | None = dc_field( + default=None, metadata=dc_config(field_name="custom") + ) @dataclass @@ -24729,6 +26148,69 @@ class RingCallResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) # List of members ringing notification was sent to members_ids: list[str] = dc_field(metadata=dc_config(field_name="members_ids")) + # The ring this call created, for correlating the accept, reject and missed outcomes that follow + ring_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="ring_id") + ) + + +@dataclass +class RingDeliveryResponse(DataClassJsonMixin): + # The transport the ring was dispatched over: ws, apns_voip, apns or fcm + channel: str = dc_field(metadata=dc_config(field_name="channel")) + # When the ring was dispatched + sent_at: datetime = dc_field( + metadata=dc_config( + field_name="sent_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # When the device acknowledged the ring; not populated yet + acked_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="acked_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # The device the ring was dispatched to, empty for websocket deliveries which fan out to every connection + device_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="device_id") + ) + # When the device displayed the ring; not populated yet + displayed_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="displayed_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # When the device fetched the call; not populated yet + fetched_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="fetched_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # When the push provider accepted the dispatch; not populated yet + provider_accepted_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="provider_accepted_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) @dataclass @@ -24773,6 +26255,41 @@ class RingSettingsResponse(DataClassJsonMixin): ) +@dataclass +class RingSettledTarget(DataClassJsonMixin): + state: str = dc_field(metadata=dc_config(field_name="state")) + state_at: datetime = dc_field( + metadata=dc_config( + field_name="state_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + reason: str | None = dc_field(default=None, metadata=dc_config(field_name="reason")) + + +@dataclass +class RingTargetResponse(DataClassJsonMixin): + # The reject or cancel reason, empty otherwise + reason: str = dc_field(metadata=dc_config(field_name="reason")) + # The target's state for this ring: pending, accepted, rejected, missed or cancelled + state: str = dc_field(metadata=dc_config(field_name="state")) + # When the target last changed state + state_at: datetime = dc_field( + metadata=dc_config( + field_name="state_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # The dispatches of this ring to the target's devices + deliveries: "list[RingDeliveryResponse]" = dc_field( + metadata=dc_config(field_name="deliveries") + ) + + @dataclass class Role(DataClassJsonMixin): # Date/time of creation @@ -24846,12 +26363,16 @@ class RuleBuilderCondition(DataClassJsonMixin): content_count_rule_params: "ContentCountRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="content_count_rule_params") ) + content_country_code_params: "ContentCountryCodeParameters | None" = dc_field( + default=None, metadata=dc_config(field_name="content_country_code_params") + ) content_custom_property_count_params: "ContentCustomPropertyCountParameters | None" = dc_field( default=None, metadata=dc_config(field_name="content_custom_property_count_params"), ) content_custom_property_params: "ContentCustomPropertyParameters | None" = dc_field( - default=None, metadata=dc_config(field_name="content_custom_property_params") + default=None, + metadata=dc_config(field_name="content_custom_property_params"), ) content_flag_count_rule_params: "FlagCountRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="content_flag_count_rule_params") @@ -24868,6 +26389,10 @@ class RuleBuilderCondition(DataClassJsonMixin): image_rule_params: "ImageRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="image_rule_params") ) + # intent_rule params: policy intent topic label and the minimum final score (1-100) that fires the condition + intent_rule_params: "IntentRuleParameters | None" = dc_field( + default=None, metadata=dc_config(field_name="intent_rule_params") + ) ip_content_count_rule_params: "IPContentCountRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="ip_content_count_rule_params") ) @@ -24889,6 +26414,14 @@ class RuleBuilderCondition(DataClassJsonMixin): text_rule_params: "TextRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="text_rule_params") ) + user_channel_ban_count_params: "UserChannelBanCountRuleParameters | None" = ( + dc_field( + default=None, metadata=dc_config(field_name="user_channel_ban_count_params") + ) + ) + user_channel_count_params: "UserChannelCountRuleParameters | None" = dc_field( + default=None, metadata=dc_config(field_name="user_channel_count_params") + ) user_created_within_params: "UserCreatedWithinParameters | None" = dc_field( default=None, metadata=dc_config(field_name="user_created_within_params") ) @@ -24902,6 +26435,12 @@ class RuleBuilderCondition(DataClassJsonMixin): default=None, metadata=dc_config(field_name="user_identical_content_count_params"), ) + user_identical_image_count_params: "UserIdenticalImageCountParameters | None" = ( + dc_field( + default=None, + metadata=dc_config(field_name="user_identical_image_count_params"), + ) + ) user_reaction_count_params: "UserReactionCountRuleParameters | None" = dc_field( default=None, metadata=dc_config(field_name="user_reaction_count_params") ) @@ -25250,53 +26789,298 @@ class SIPInboundRoutingRuleResponse(DataClassJsonMixin): pin_protection_configs: "SIPPinProtectionConfigsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="pin_protection_configs") ) - # PIN routing rule call configuration response - pin_routing_configs: "SIPInboundRoutingRulePinConfigsResponse | None" = dc_field( - default=None, metadata=dc_config(field_name="pin_routing_configs") + # PIN routing rule call configuration response + pin_routing_configs: "SIPInboundRoutingRulePinConfigsResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="pin_routing_configs") + ) + + +@dataclass +class SIPPinProtectionConfigsRequest(DataClassJsonMixin): + # Default PIN to use if there is no PIN set on the call object + default_pin: str | None = dc_field( + default=None, metadata=dc_config(field_name="default_pin") + ) + # Whether PIN protection is enabled + enabled: bool | None = dc_field( + default=None, metadata=dc_config(field_name="enabled") + ) + # Maximum number of PIN attempts allowed + max_attempts: int | None = dc_field( + default=None, metadata=dc_config(field_name="max_attempts") + ) + # Number of digits required for the PIN + required_pin_digits: int | None = dc_field( + default=None, metadata=dc_config(field_name="required_pin_digits") + ) + + +@dataclass +class SIPPinProtectionConfigsResponse(DataClassJsonMixin): + # Whether PIN protection is enabled + enabled: bool = dc_field(metadata=dc_config(field_name="enabled")) + # Default PIN to use if there is no PIN set on the call object + default_pin: str | None = dc_field( + default=None, metadata=dc_config(field_name="default_pin") + ) + # Maximum number of PIN attempts allowed + max_attempts: int | None = dc_field( + default=None, metadata=dc_config(field_name="max_attempts") + ) + # Number of digits required for the PIN + required_pin_digits: int | None = dc_field( + default=None, metadata=dc_config(field_name="required_pin_digits") + ) + + +@dataclass +class SIPTrunkResponse(DataClassJsonMixin): + # Creation timestamp + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # Unique identifier for the SIP trunk + id: str = dc_field(metadata=dc_config(field_name="id")) + # Name of the SIP trunk + name: str = dc_field(metadata=dc_config(field_name="name")) + # Password for SIP trunk authentication + password: str = dc_field(metadata=dc_config(field_name="password")) + # Last update timestamp + updated_at: datetime = dc_field( + metadata=dc_config( + field_name="updated_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # The URI for the SIP trunk + uri: str = dc_field(metadata=dc_config(field_name="uri")) + # Username for SIP trunk authentication + username: str = dc_field(metadata=dc_config(field_name="username")) + # Allowed IPv4/IPv6 addresses or CIDR blocks + allowed_ips: list[str] = dc_field(metadata=dc_config(field_name="allowed_ips")) + # Phone numbers associated with this SIP trunk + numbers: list[str] = dc_field(metadata=dc_config(field_name="numbers")) + + +@dataclass +class SRTIngress(DataClassJsonMixin): + address: str = dc_field(metadata=dc_config(field_name="address")) + + +@dataclass +class ScheduledMessageChannelResponse(DataClassJsonMixin): + # Channel CID (:) + cid: str = dc_field(metadata=dc_config(field_name="cid")) + # Channel ID + id: str = dc_field(metadata=dc_config(field_name="id")) + # Channel type + type: str = dc_field(metadata=dc_config(field_name="type")) + # Channel image, from its custom data + image: str | None = dc_field(default=None, metadata=dc_config(field_name="image")) + # Channel name, from its custom data + name: str | None = dc_field(default=None, metadata=dc_config(field_name="name")) + + +@dataclass +class ScheduledMessageContentResponse(DataClassJsonMixin): + # Text of the message + text: str = dc_field(metadata=dc_config(field_name="text")) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + # Contains HTML markup of the message + html: str | None = dc_field(default=None, metadata=dc_config(field_name="html")) + # Whether the message mentions the whole channel + mentioned_channel: bool | None = dc_field( + default=None, metadata=dc_config(field_name="mentioned_channel") + ) + # Whether the message mentions the online channel members + mentioned_here: bool | None = dc_field( + default=None, metadata=dc_config(field_name="mentioned_here") + ) + # MML content of the message + mml: str | None = dc_field(default=None, metadata=dc_config(field_name="mml")) + # ID of parent message (thread) + parent_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="parent_id") + ) + # Date/time the pin expires + pin_expires: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="pin_expires", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # Whether the message will be pinned when sent + pinned: bool | None = dc_field( + default=None, metadata=dc_config(field_name="pinned") + ) + # Identifier of the poll to include in the message + poll_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="poll_id") + ) + quoted_message_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="quoted_message_id") + ) + # Whether thread reply should be shown in the channel as well + show_in_channel: bool | None = dc_field( + default=None, metadata=dc_config(field_name="show_in_channel") + ) + # Whether message is silent or not + silent: bool | None = dc_field( + default=None, metadata=dc_config(field_name="silent") + ) + # Contains type of the message. One of: regular, system + type: str | None = dc_field(default=None, metadata=dc_config(field_name="type")) + # Array of message attachments + attachments: "list[Attachment] | None" = dc_field( + default=None, metadata=dc_config(field_name="attachments") + ) + # IDs of the user groups mentioned in the message + mentioned_group_ids: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="mentioned_group_ids") + ) + # Roles mentioned in the message + mentioned_roles: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="mentioned_roles") + ) + # IDs of the users mentioned in the message + mentioned_users: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="mentioned_users") + ) + # IDs of the users the message will be visible to + restricted_visibility: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="restricted_visibility") + ) + # The location a scheduled message will share + shared_location: "ScheduledMessageLocation | None" = dc_field( + default=None, metadata=dc_config(field_name="shared_location") + ) + + +@dataclass +class ScheduledMessageFailedEvent(DataClassJsonMixin): + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + user: "UserResponseCommonFields" = dc_field(metadata=dc_config(field_name="user")) + # The type of event: "scheduled_message.failed" in this case + type: str = dc_field( + default="scheduled_message.failed", metadata=dc_config(field_name="type") + ) + # The CID of the channel the message is scheduled in + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + received_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="received_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="scheduled_message") ) @dataclass -class SIPPinProtectionConfigsRequest(DataClassJsonMixin): - # Default PIN to use if there is no PIN set on the call object - default_pin: str | None = dc_field( - default=None, metadata=dc_config(field_name="default_pin") +class ScheduledMessageLocation(DataClassJsonMixin): + # The ID of the device that created the location. + created_by_device_id: str = dc_field( + metadata=dc_config(field_name="created_by_device_id") ) - # Whether PIN protection is enabled - enabled: bool | None = dc_field( - default=None, metadata=dc_config(field_name="enabled") + # The date when the location sharing ends. + # If it's empty, it means the location sharing is static instead of live. + end_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="end_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), ) - # Maximum number of PIN attempts allowed - max_attempts: int | None = dc_field( - default=None, metadata=dc_config(field_name="max_attempts") + # The latitude of the location. + latitude: float | None = dc_field( + default=None, metadata=dc_config(field_name="latitude") ) - # Number of digits required for the PIN - required_pin_digits: int | None = dc_field( - default=None, metadata=dc_config(field_name="required_pin_digits") + # The longitude of the location. + longitude: float | None = dc_field( + default=None, metadata=dc_config(field_name="longitude") ) @dataclass -class SIPPinProtectionConfigsResponse(DataClassJsonMixin): - # Whether PIN protection is enabled - enabled: bool = dc_field(metadata=dc_config(field_name="enabled")) - # Default PIN to use if there is no PIN set on the call object - default_pin: str | None = dc_field( - default=None, metadata=dc_config(field_name="default_pin") +class ScheduledMessageResponse(DataClassJsonMixin): + # The CID of the channel the message will be sent to + cid: str = dc_field(metadata=dc_config(field_name="cid")) + # Date/time of creation + created_at: datetime = dc_field( + metadata=dc_config( + field_name="created_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) ) - # Maximum number of PIN attempts allowed - max_attempts: int | None = dc_field( - default=None, metadata=dc_config(field_name="max_attempts") + # Unique identifier of the scheduled message + id: str = dc_field(metadata=dc_config(field_name="id")) + # Date/time the message is scheduled to be sent + scheduled_at: datetime = dc_field( + metadata=dc_config( + field_name="scheduled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) ) - # Number of digits required for the PIN - required_pin_digits: int | None = dc_field( - default=None, metadata=dc_config(field_name="required_pin_digits") + # Status of the scheduled message. One of: scheduled, sending, failed, canceled, sent. A message is sending from its first send attempt until it is sent or fails, including while it waits to retry a failed attempt; it cannot be edited, canceled or sent now while sending + status: str = dc_field(metadata=dc_config(field_name="status")) + # Date/time of the last update + updated_at: datetime = dc_field( + metadata=dc_config( + field_name="updated_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ) + ) + # The message a scheduled message will send + message: "ScheduledMessageContentResponse" = dc_field( + metadata=dc_config(field_name="message") + ) + # User response object + user: "UserResponse" = dc_field(metadata=dc_config(field_name="user")) + # Why sending the message failed. Only set when status is failed. One of: banned, no_permission, channel_frozen, channel_deleted, user_deleted, moderation_blocked, hook_rejected, expired, internal_error, invalid_message, pending_not_supported + failure_reason: str | None = dc_field( + default=None, metadata=dc_config(field_name="failure_reason") + ) + # The channel of a scheduled message + channel: "ScheduledMessageChannelResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="channel") ) @dataclass -class SIPTrunkResponse(DataClassJsonMixin): - # Creation timestamp +class ScheduledMessageSentEvent(DataClassJsonMixin): + # Date/time of creation created_at: datetime = dc_field( metadata=dc_config( field_name="created_at", @@ -25305,34 +27089,39 @@ class SIPTrunkResponse(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) - # Unique identifier for the SIP trunk - id: str = dc_field(metadata=dc_config(field_name="id")) - # Name of the SIP trunk - name: str = dc_field(metadata=dc_config(field_name="name")) - # Password for SIP trunk authentication - password: str = dc_field(metadata=dc_config(field_name="password")) - # Last update timestamp - updated_at: datetime = dc_field( + # The ID of the message that was sent + message_id: str = dc_field(metadata=dc_config(field_name="message_id")) + custom: dict[str, object] = dc_field(metadata=dc_config(field_name="custom")) + user: "UserResponseCommonFields" = dc_field(metadata=dc_config(field_name="user")) + # The type of event: "scheduled_message.sent" in this case + type: str = dc_field( + default="scheduled_message.sent", metadata=dc_config(field_name="type") + ) + # The CID of the channel the message was sent to + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + received_at: datetime | None = dc_field( + default=None, metadata=dc_config( - field_name="updated_at", + field_name="received_at", encoder=encode_datetime, decoder=datetime_from_unix_ns, mm_field=fields.DateTime(format="iso"), - ) + ), + ) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="scheduled_message") ) - # The URI for the SIP trunk - uri: str = dc_field(metadata=dc_config(field_name="uri")) - # Username for SIP trunk authentication - username: str = dc_field(metadata=dc_config(field_name="username")) - # Allowed IPv4/IPv6 addresses or CIDR blocks - allowed_ips: list[str] = dc_field(metadata=dc_config(field_name="allowed_ips")) - # Phone numbers associated with this SIP trunk - numbers: list[str] = dc_field(metadata=dc_config(field_name="numbers")) @dataclass -class SRTIngress(DataClassJsonMixin): - address: str = dc_field(metadata=dc_config(field_name="address")) +class ScheduledMessagesFilter(DataClassJsonMixin): + # Only scheduled messages of this channel + cid: str | None = dc_field(default=None, metadata=dc_config(field_name="cid")) + # Only these statuses, as reported in each scheduled message's status. One or more of: scheduled, sending, failed + status: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="status") + ) @dataclass @@ -25847,6 +27636,16 @@ class SendMessageRequest(DataClassJsonMixin): pending: bool | None = dc_field( default=None, metadata=dc_config(field_name="pending") ) + # When set, the message is not sent now: it is stored and sent at this time (UTC). The response then carries scheduled_message. Must be at least 1 minute and at most the app's maximum lead time (120 days by default) from now. Slash commands cannot be scheduled + scheduled_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="scheduled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) skip_enrich_url: bool | None = dc_field( default=None, metadata=dc_config(field_name="skip_enrich_url") ) @@ -25876,6 +27675,10 @@ class SendMessageResponse(DataClassJsonMixin): pending_message_metadata: "dict[str, str] | None" = dc_field( default=None, metadata=dc_config(field_name="pending_message_metadata") ) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse | None" = dc_field( + default=None, metadata=dc_config(field_name="scheduled_message") + ) @dataclass @@ -25901,6 +27704,21 @@ class SendReactionResponse(DataClassJsonMixin): reaction: "ReactionResponse" = dc_field(metadata=dc_config(field_name="reaction")) +@dataclass +class SendScheduledMessageNowRequest(DataClassJsonMixin): + pass + + +@dataclass +class SendScheduledMessageNowResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse" = dc_field( + metadata=dc_config(field_name="scheduled_message") + ) + + @dataclass class SendUserCustomEventRequest(DataClassJsonMixin): event: "UserCustomEventRequest" = dc_field(metadata=dc_config(field_name="event")) @@ -25957,6 +27775,26 @@ class SessionWarningResponse(DataClassJsonMixin): ) +@dataclass +class SetFeedsRetentionPolicyRequest(DataClassJsonMixin): + max_age_hours: int = dc_field(metadata=dc_config(field_name="max_age_hours")) + policy: str = dc_field(metadata=dc_config(field_name="policy")) + enabled: bool | None = dc_field( + default=None, metadata=dc_config(field_name="enabled") + ) + feed_groups: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="feed_groups") + ) + + +@dataclass +class SetFeedsRetentionPolicyResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + enabled: bool = dc_field(metadata=dc_config(field_name="enabled")) + policy: "FeedsRetentionPolicy" = dc_field(metadata=dc_config(field_name="policy")) + + @dataclass class SetRetentionPolicyRequest(DataClassJsonMixin): max_age_hours: int = dc_field(metadata=dc_config(field_name="max_age_hours")) @@ -26102,7 +27940,9 @@ class SharedLocationResponse(DataClassJsonMixin): @dataclass class SharedLocationResponseData(DataClassJsonMixin): + # The CID (type:id) of the channel that the location is attached to. channel_cid: str = dc_field(metadata=dc_config(field_name="channel_cid")) + # The date when the location was created. created_at: datetime = dc_field( metadata=dc_config( field_name="created_at", @@ -26111,12 +27951,17 @@ class SharedLocationResponseData(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + # The ID of the device that created the location. created_by_device_id: str = dc_field( metadata=dc_config(field_name="created_by_device_id") ) + # The latitude of the location. latitude: float = dc_field(metadata=dc_config(field_name="latitude")) + # The longitude of the location. longitude: float = dc_field(metadata=dc_config(field_name="longitude")) + # The ID of the message that the location is attached to. message_id: str = dc_field(metadata=dc_config(field_name="message_id")) + # The date when the location was updated. updated_at: datetime = dc_field( metadata=dc_config( field_name="updated_at", @@ -26125,7 +27970,10 @@ class SharedLocationResponseData(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + # The ID of the user that created the location. user_id: str = dc_field(metadata=dc_config(field_name="user_id")) + # The date when the location sharing ends. + # If it's empty, it means the location sharing is static instead of live. end_at: datetime | None = dc_field( default=None, metadata=dc_config( @@ -26568,7 +28416,7 @@ class StoriesFeedUpdatedEvent(DataClassJsonMixin): @dataclass class SubmitActionRequest(DataClassJsonMixin): - # Type of moderation action to perform. One of: mark_reviewed, delete_message, delete_activity, delete_comment, delete_reaction, ban, custom, unban, restore, delete_user, delete_user_messages, unblock, block, shadow_block, unmask, kick_user, end_call, escalate, de_escalate + # Type of moderation action to perform. One of: mark_reviewed, delete_message, delete_activity, delete_comment, delete_reaction, delete_poll, delete_poll_option, delete_poll_answer, ban, custom, unban, restore, delete_user, delete_user_messages, unblock, block, shadow_block, unmask, kick_user, end_call, escalate, de_escalate action_type: str = dc_field(metadata=dc_config(field_name="action_type")) # UUID of the appeal to act on (required for reject_appeal, optional for other actions) appeal_id: str | None = dc_field( @@ -26608,6 +28456,18 @@ class SubmitActionRequest(DataClassJsonMixin): delete_message: "DeleteMessageRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="delete_message") ) + # Configuration for poll deletion action. Poll deletion is always permanent. + delete_poll: "DeletePollRequestPayload | None" = dc_field( + default=None, metadata=dc_config(field_name="delete_poll") + ) + # Configuration for deleting the free-text poll answer a review item was raised for. Chat polls only. Deletion is permanent; the poll and other votes are kept. + delete_poll_answer: "DeletePollAnswerRequestPayload | None" = dc_field( + default=None, metadata=dc_config(field_name="delete_poll_answer") + ) + # Configuration for deleting the single poll option a review item was raised for. Deletion is permanent; the rest of the poll is kept. + delete_poll_option: "DeletePollOptionRequestPayload | None" = dc_field( + default=None, metadata=dc_config(field_name="delete_poll_option") + ) # Configuration for reaction deletion action delete_reaction: "DeleteReactionRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="delete_reaction") @@ -26647,7 +28507,8 @@ class SubmitActionRequest(DataClassJsonMixin): unban: "UnbanActionRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="unban") ) - # Deprecated: use restore instead โ€” it now also reverses a block or shadow block. Configuration for unblock action. + # Deprecated: Use restore instead, which now also reverses a block or shadow block. Configuration for unblock action. + # Deprecated unblock: "UnblockActionRequestPayload | None" = dc_field( default=None, metadata=dc_config(field_name="unblock") ) @@ -27190,6 +29051,7 @@ class ThreadedCommentResponse(DataClassJsonMixin): ) ) downvote_count: int = dc_field(metadata=dc_config(field_name="downvote_count")) + hidden: bool = dc_field(metadata=dc_config(field_name="hidden")) id: str = dc_field(metadata=dc_config(field_name="id")) object_id: str = dc_field(metadata=dc_config(field_name="object_id")) object_type: str = dc_field(metadata=dc_config(field_name="object_type")) @@ -27582,6 +29444,7 @@ class UnbanRequest(DataClassJsonMixin): default=None, metadata=dc_config(field_name="unbanned_by_id") ) # User request object + # Deprecated unbanned_by: "UserRequest | None" = dc_field( default=None, metadata=dc_config(field_name="unbanned_by") ) @@ -27667,6 +29530,8 @@ class UndeleteMessageResponse(DataClassJsonMixin): class UnfollowBatchRequest(DataClassJsonMixin): # List of follow relationships to remove, each with optional keep_history follows: "list[UnfollowPair]" = dc_field(metadata=dc_config(field_name="follows")) + # If true, the batch is queued as a background task, paced per app, and the response has empty follows and a task_id. Server-side only. Supported by the get-or-create endpoint (GetOrCreateUnfollows); UnfollowBatch rejects it. Async batches can apply out of order with other follow writes. Defaults to false. + _async: bool | None = dc_field(default=None, metadata=dc_config(field_name="async")) # Whether to delete the corresponding notification activity (default: false) delete_notification_activity: bool | None = dc_field( default=None, metadata=dc_config(field_name="delete_notification_activity") @@ -27682,6 +29547,10 @@ class UnfollowBatchResponse(DataClassJsonMixin): duration: str = dc_field(metadata=dc_config(field_name="duration")) # List of follow relationships that were removed follows: "list[FollowResponse]" = dc_field(metadata=dc_config(field_name="follows")) + # ID of the background task when the request was sent with async: true + task_id: str | None = dc_field( + default=None, metadata=dc_config(field_name="task_id") + ) @dataclass @@ -27783,7 +29652,9 @@ class UnreadCountsBatchResponse(DataClassJsonMixin): @dataclass class UnreadCountsChannel(DataClassJsonMixin): + # The channel CID (type:id). channel_id: str = dc_field(metadata=dc_config(field_name="channel_id")) + # The date which the current user last read the channel. last_read: datetime = dc_field( metadata=dc_config( field_name="last_read", @@ -27792,13 +29663,17 @@ class UnreadCountsChannel(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + # The number of unread messages inside the channel. unread_count: int = dc_field(metadata=dc_config(field_name="unread_count")) @dataclass class UnreadCountsChannelType(DataClassJsonMixin): + # The number of unread channels of this channel type. channel_count: int = dc_field(metadata=dc_config(field_name="channel_count")) + # The channel type. channel_type: str = dc_field(metadata=dc_config(field_name="channel_type")) + # The number of unread messages of all the channels with this type. unread_count: int = dc_field(metadata=dc_config(field_name="unread_count")) @@ -27826,6 +29701,7 @@ class UnreadCountsResponse(DataClassJsonMixin): @dataclass class UnreadCountsThread(DataClassJsonMixin): + # The date which the current user last read the thread. last_read: datetime = dc_field( metadata=dc_config( field_name="last_read", @@ -27834,12 +29710,15 @@ class UnreadCountsThread(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ) ) + # The id of the last reply which the current user read in the thread. last_read_message_id: str = dc_field( metadata=dc_config(field_name="last_read_message_id") ) + # The message id of the root of the thread. parent_message_id: str = dc_field( metadata=dc_config(field_name="parent_message_id") ) + # The number of unread replies inside the thread. unread_count: int = dc_field(metadata=dc_config(field_name="unread_count")) @@ -28642,6 +30521,7 @@ class UpdateChannelTypeRequest(DataClassJsonMixin): default=None, metadata=dc_config(field_name="partition_ttl") ) polls: bool | None = dc_field(default=None, metadata=dc_config(field_name="polls")) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -28726,6 +30606,7 @@ class UpdateChannelTypeResponse(DataClassJsonMixin): ) custom_events: bool = dc_field(metadata=dc_config(field_name="custom_events")) delivery_events: bool = dc_field(metadata=dc_config(field_name="delivery_events")) + # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) mark_messages_pending: bool = dc_field( metadata=dc_config(field_name="mark_messages_pending") @@ -28783,6 +30664,7 @@ class UpdateChannelTypeResponse(DataClassJsonMixin): partition_ttl: str | None = dc_field( default=None, metadata=dc_config(field_name="partition_ttl") ) + # Sets the push notification level for a channel type push_level: str | None = dc_field( default=None, metadata=dc_config(field_name="push_level") ) @@ -29367,6 +31249,14 @@ class UpdatePollOptionRequest(DataClassJsonMixin): id: str = dc_field(metadata=dc_config(field_name="id")) # Option text text: str = dc_field(metadata=dc_config(field_name="text")) + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) @@ -29382,6 +31272,14 @@ class UpdatePollOptionRequest(DataClassJsonMixin): @dataclass class UpdatePollPartialRequest(DataClassJsonMixin): + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) user_id: str | None = dc_field( default=None, metadata=dc_config(field_name="user_id") ) @@ -29413,6 +31311,14 @@ class UpdatePollRequest(DataClassJsonMixin): allow_user_suggested_options: bool | None = dc_field( default=None, metadata=dc_config(field_name="allow_user_suggested_options") ) + # Key of the moderation configuration to use. Server-side only. + config_key: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_key") + ) + # Team associated with the configuration. Server-side only. + config_team: str | None = dc_field( + default=None, metadata=dc_config(field_name="config_team") + ) # Poll description description: str | None = dc_field( default=None, metadata=dc_config(field_name="description") @@ -29497,6 +31403,15 @@ class UpdateQueueRequest(DataClassJsonMixin): @dataclass class UpdateReminderRequest(DataClassJsonMixin): + expires_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="expires_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) remind_at: datetime | None = dc_field( default=None, metadata=dc_config( @@ -29594,6 +31509,34 @@ class UpdateSIPTrunkResponse(DataClassJsonMixin): ) +@dataclass +class UpdateScheduledMessageRequest(DataClassJsonMixin): + # New send time (UTC). Must be at least 1 minute and at most the app's maximum lead time from now + scheduled_at: datetime | None = dc_field( + default=None, + metadata=dc_config( + field_name="scheduled_at", + encoder=encode_datetime, + decoder=datetime_from_unix_ns, + mm_field=fields.DateTime(format="iso"), + ), + ) + # Message data for creating or updating a message + message: "MessageRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="message") + ) + + +@dataclass +class UpdateScheduledMessageResponse(DataClassJsonMixin): + # Duration of the request in milliseconds + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # A message scheduled to be sent later + scheduled_message: "ScheduledMessageResponse" = dc_field( + metadata=dc_config(field_name="scheduled_message") + ) + + @dataclass class UpdateSegmentRequest(DataClassJsonMixin): # The description of the segment (max 256 characters) @@ -29708,7 +31651,7 @@ class UpdateUsersRequest(DataClassJsonMixin): class UpdateUsersResponse(DataClassJsonMixin): # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) - # Deprecated: always empty. Removing a user from a team no longer deletes their memberships in that team's channels, so there is no task to poll + # Deprecated: Always empty. Removing a user from a team no longer deletes their memberships in that team's channels, so there is no task to poll. # Deprecated membership_deletion_task_id: str = dc_field( metadata=dc_config(field_name="membership_deletion_task_id") @@ -29874,6 +31817,10 @@ class UpsertActivitiesRequest(DataClassJsonMixin): force_moderation: bool | None = dc_field( default=None, metadata=dc_config(field_name="force_moderation") ) + # Server-side only. If true, activities whose id already exists are left unchanged instead of updated: no write, events or notifications. They are listed in skipped_existing and returned as stored. Soft-deleted activities count as existing and are returned with deleted_at set. Default: false. + skip_existing: bool | None = dc_field( + default=None, metadata=dc_config(field_name="skip_existing") + ) @dataclass @@ -29887,6 +31834,10 @@ class UpsertActivitiesResponse(DataClassJsonMixin): mention_notifications_created: int | None = dc_field( default=None, metadata=dc_config(field_name="mention_notifications_created") ) + # IDs of activities left unchanged because they already existed. Only set when skip_existing is true. + skipped_existing: list[str] | None = dc_field( + default=None, metadata=dc_config(field_name="skipped_existing") + ) @dataclass @@ -29935,7 +31886,8 @@ class UpsertConfigRequest(DataClassJsonMixin): metadata=dc_config(field_name="automod_platform_circumvention_config"), ) automod_semantic_filters_config: "AutomodSemanticFiltersConfig | None" = dc_field( - default=None, metadata=dc_config(field_name="automod_semantic_filters_config") + default=None, + metadata=dc_config(field_name="automod_semantic_filters_config"), ) automod_toxicity_config: "AutomodToxicityConfig | None" = dc_field( default=None, metadata=dc_config(field_name="automod_toxicity_config") @@ -29955,6 +31907,9 @@ class UpsertConfigRequest(DataClassJsonMixin): google_vision_config: "GoogleVisionConfig | None" = dc_field( default=None, metadata=dc_config(field_name="google_vision_config") ) + intent_config: "IntentConfigRequest | None" = dc_field( + default=None, metadata=dc_config(field_name="intent_config") + ) llm_config: "LLMConfig | None" = dc_field( default=None, metadata=dc_config(field_name="llm_config") ) @@ -30205,6 +32160,23 @@ class UpsertSetupSessionResponse(DataClassJsonMixin): ) +@dataclass +class UpsertUserInterestsRequest(DataClassJsonMixin): + # Interest tags to add or update (1-50) + interests: "list[UserInterestRequest]" = dc_field( + metadata=dc_config(field_name="interests") + ) + + +@dataclass +class UpsertUserInterestsResponse(DataClassJsonMixin): + duration: str = dc_field(metadata=dc_config(field_name="duration")) + # All interest tags of the user after the write + interests: "list[InterestTagResponse]" = dc_field( + metadata=dc_config(field_name="interests") + ) + + @dataclass class User(DataClassJsonMixin): id: str = dc_field(metadata=dc_config(field_name="id")) @@ -30286,6 +32258,29 @@ class UserBannedEvent(DataClassJsonMixin): ) +@dataclass +class UserChannelBanCountRuleParameters(DataClassJsonMixin): + banned_by: str | None = dc_field( + default=None, metadata=dc_config(field_name="banned_by") + ) + threshold: int | None = dc_field( + default=None, metadata=dc_config(field_name="threshold") + ) + time_window: str | None = dc_field( + default=None, metadata=dc_config(field_name="time_window") + ) + + +@dataclass +class UserChannelCountRuleParameters(DataClassJsonMixin): + threshold: int | None = dc_field( + default=None, metadata=dc_config(field_name="threshold") + ) + time_window: str | None = dc_field( + default=None, metadata=dc_config(field_name="time_window") + ) + + @dataclass class UserCreatedWithinParameters(DataClassJsonMixin): max_age: str | None = dc_field( @@ -30718,6 +32713,30 @@ class UserIdenticalContentCountParameters(DataClassJsonMixin): ) +@dataclass +class UserIdenticalImageCountParameters(DataClassJsonMixin): + match: str | None = dc_field(default=None, metadata=dc_config(field_name="match")) + similarity_distance: int | None = dc_field( + default=None, metadata=dc_config(field_name="similarity_distance") + ) + threshold: int | None = dc_field( + default=None, metadata=dc_config(field_name="threshold") + ) + time_window: str | None = dc_field( + default=None, metadata=dc_config(field_name="time_window") + ) + + +@dataclass +class UserInterestRequest(DataClassJsonMixin): + # The interest tag; trimmed and lower-cased like activity interest_tags + tag: str = dc_field(metadata=dc_config(field_name="tag")) + # Ranking weight between -1.0 (dislike) and 1.0 (like). Defaults to 1.0 + weight: float | None = dc_field( + default=None, metadata=dc_config(field_name="weight") + ) + + @dataclass class UserMessagesDeletedEvent(DataClassJsonMixin): # Date/time of creation @@ -30849,6 +32868,7 @@ class UserRatingReportResponse(DataClassJsonMixin): @dataclass class UserReactionCountRuleParameters(DataClassJsonMixin): + count: str | None = dc_field(default=None, metadata=dc_config(field_name="count")) threshold: int | None = dc_field( default=None, metadata=dc_config(field_name="threshold") ) @@ -30912,6 +32932,7 @@ class UserRequest(DataClassJsonMixin): custom: dict[str, object] | None = dc_field( default=None, metadata=dc_config(field_name="custom") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -31024,6 +33045,7 @@ class UserResponse(DataClassJsonMixin): devices: "list[DeviceResponse] | None" = dc_field( default=None, metadata=dc_config(field_name="devices") ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -31181,6 +33203,7 @@ class UserResponsePrivacyFields(DataClassJsonMixin): mm_field=fields.DateTime(format="iso"), ), ) + # The privacy settings of the user. privacy_settings: "PrivacySettingsResponse | None" = dc_field( default=None, metadata=dc_config(field_name="privacy_settings") ) @@ -31638,6 +33661,7 @@ class WSEvent(DataClassJsonMixin): watcher_count: int | None = dc_field( default=None, metadata=dc_config(field_name="watcher_count") ) + # Response from AI moderation automoderation_scores: "ModerationResponse | None" = dc_field( default=None, metadata=dc_config(field_name="automoderation_scores") ) @@ -31716,21 +33740,27 @@ class WebhookFailoverConfig(DataClassJsonMixin): class WrappedUnreadCountsResponse(DataClassJsonMixin): # Duration of the request in milliseconds duration: str = dc_field(metadata=dc_config(field_name="duration")) + # The total number of unread messages. total_unread_count: int = dc_field( metadata=dc_config(field_name="total_unread_count") ) + # The total number of unread threads. total_unread_threads_count: int = dc_field( metadata=dc_config(field_name="total_unread_threads_count") ) + # The unread information per channel type. channel_type: "list[UnreadCountsChannelType]" = dc_field( metadata=dc_config(field_name="channel_type") ) + # The unread information per channel. channels: "list[UnreadCountsChannel]" = dc_field( metadata=dc_config(field_name="channels") ) + # The unread information per thread. threads: "list[UnreadCountsThread]" = dc_field( metadata=dc_config(field_name="threads") ) + # The total number of unread messages grouped by team. total_unread_count_by_team: "dict[str, int] | None" = dc_field( default=None, metadata=dc_config(field_name="total_unread_count_by_team") ) diff --git a/getstream/moderation/async_rest_client.py b/getstream/moderation/async_rest_client.py index 7284b3b0..88eab0ea 100644 --- a/getstream/moderation/async_rest_client.py +++ b/getstream/moderation/async_rest_client.py @@ -41,11 +41,13 @@ async def get_action_config( user_id: Optional[str] = None, ) -> StreamResponse[GetActionConfigResponse]: query_params = build_query_param( - queue_type=queue_type, - entity_type=entity_type, - exclude_defaults=exclude_defaults, - only_defaults=only_defaults, - user_id=user_id, + **{ + "queue_type": queue_type, + "entity_type": entity_type, + "exclude_defaults": exclude_defaults, + "only_defaults": only_defaults, + "user_id": user_id, + } ) return await self.get( "/api/v2/moderation/action_config", @@ -119,7 +121,7 @@ async def bulk_delete_action_config( async def delete_action_config( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteActionConfigResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -162,6 +164,7 @@ async def analyze( async_response: Optional[bool] = None, config_key: Optional[str] = None, content_published_at: Optional[datetime] = None, + country_code: Optional[str] = None, entity_creator_id: Optional[str] = None, entity_id: Optional[str] = None, entity_type: Optional[str] = None, @@ -175,6 +178,7 @@ async def analyze( async_response=async_response, config_key=config_key, content_published_at=content_published_at, + country_code=country_code, entity_creator_id=entity_creator_id, entity_id=entity_id, entity_type=entity_type, @@ -192,6 +196,7 @@ async def appeal( appeal_reason: str, entity_id: str, entity_type: str, + channel_cid: Optional[str] = None, review_queue_item_id: Optional[str] = None, user_id: Optional[str] = None, attachments: Optional[List[str]] = None, @@ -201,6 +206,7 @@ async def appeal( appeal_reason=appeal_reason, entity_id=entity_id, entity_type=entity_type, + channel_cid=channel_cid, review_queue_item_id=review_queue_item_id, user_id=user_id, attachments=attachments, @@ -275,9 +281,11 @@ async def bulk_action_appeals( async def ban( self, target_user_id: str, + ban_from_future_channels: Optional[bool] = None, banned_by_id: Optional[str] = None, channel_cid: Optional[str] = None, delete_messages: Optional[str] = None, + delete_reactions: Optional[bool] = None, ip_ban: Optional[bool] = None, reason: Optional[str] = None, shadow: Optional[bool] = None, @@ -286,9 +294,11 @@ async def ban( ) -> StreamResponse[ModerationBanResponse]: json = BanRequest( target_user_id=target_user_id, + ban_from_future_channels=ban_from_future_channels, banned_by_id=banned_by_id, channel_cid=channel_cid, delete_messages=delete_messages, + delete_reactions=delete_reactions, ip_ban=ip_ban, reason=reason, shadow=shadow, @@ -326,6 +336,7 @@ async def check( config_key: Optional[str] = None, config_team: Optional[str] = None, content_published_at: Optional[datetime] = None, + country_code: Optional[str] = None, test_mode: Optional[bool] = None, user_id: Optional[str] = None, config: Optional[ModerationConfig] = None, @@ -340,6 +351,7 @@ async def check( config_key=config_key, config_team=config_team, content_published_at=content_published_at, + country_code=country_code, test_mode=test_mode, user_id=user_id, config=config, @@ -379,6 +391,7 @@ async def upsert_config( bodyguard_config: Optional[AITextConfig] = None, flood_config: Optional[FloodConfig] = None, google_vision_config: Optional[GoogleVisionConfig] = None, + intent_config: Optional[IntentConfigRequest] = None, llm_config: Optional[LLMConfig] = None, rule_builder_config: Optional[RuleBuilderConfig] = None, user: Optional[UserRequest] = None, @@ -402,6 +415,7 @@ async def upsert_config( bodyguard_config=bodyguard_config, flood_config=flood_config, google_vision_config=google_vision_config, + intent_config=intent_config, llm_config=llm_config, rule_builder_config=rule_builder_config, user=user, @@ -416,7 +430,7 @@ async def upsert_config( async def delete_config( self, key: str, team: Optional[str] = None, user_id: Optional[str] = None ) -> StreamResponse[DeleteModerationConfigResponse]: - query_params = build_query_param(team=team, user_id=user_id) + query_params = build_query_param(**{"team": team, "user_id": user_id}) path_params = { "key": key, } @@ -431,7 +445,7 @@ async def delete_config( async def get_config( self, key: str, team: Optional[str] = None ) -> StreamResponse[GetConfigResponse]: - query_params = build_query_param(team=team) + query_params = build_query_param(**{"team": team}) path_params = { "key": key, } @@ -684,7 +698,7 @@ async def upsert_moderation_rule( async def delete_moderation_rule( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteModerationRuleResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -824,7 +838,7 @@ async def start_policy_test_run( async def list_queues( self, user_id: Optional[str] = None ) -> StreamResponse[ListQueuesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return await self.get( "/api/v2/moderation/queues", ListQueuesResponse, query_params=query_params ) @@ -855,7 +869,7 @@ async def create_queue( async def get_queue( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[QueueResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -989,6 +1003,9 @@ async def submit_action( delete_activity: Optional[DeleteActivityRequestPayload] = None, delete_comment: Optional[DeleteCommentRequestPayload] = None, delete_message: Optional[DeleteMessageRequestPayload] = None, + delete_poll: Optional[DeletePollRequestPayload] = None, + delete_poll_answer: Optional[DeletePollAnswerRequestPayload] = None, + delete_poll_option: Optional[DeletePollOptionRequestPayload] = None, delete_reaction: Optional[DeleteReactionRequestPayload] = None, delete_user: Optional[DeleteUserRequestPayload] = None, delete_user_messages: Optional[DeleteUserMessagesRequestPayload] = None, @@ -1014,6 +1031,9 @@ async def submit_action( delete_activity=delete_activity, delete_comment=delete_comment, delete_message=delete_message, + delete_poll=delete_poll, + delete_poll_answer=delete_poll_answer, + delete_poll_option=delete_poll_option, delete_reaction=delete_reaction, delete_user=delete_user, delete_user_messages=delete_user_messages, @@ -1071,9 +1091,11 @@ async def unban( unbanned_by: Optional[UserRequest] = None, ) -> StreamResponse[UnbanResponse]: query_params = build_query_param( - target_user_id=target_user_id, - channel_cid=channel_cid, - created_by=created_by, + **{ + "target_user_id": target_user_id, + "channel_cid": channel_cid, + "created_by": created_by, + } ) json = UnbanRequest( unbanned_by_id=unbanned_by_id, unbanned_by=unbanned_by diff --git a/getstream/moderation/rest_client.py b/getstream/moderation/rest_client.py index 3ee39076..c794d9ac 100644 --- a/getstream/moderation/rest_client.py +++ b/getstream/moderation/rest_client.py @@ -41,11 +41,13 @@ def get_action_config( user_id: Optional[str] = None, ) -> StreamResponse[GetActionConfigResponse]: query_params = build_query_param( - queue_type=queue_type, - entity_type=entity_type, - exclude_defaults=exclude_defaults, - only_defaults=only_defaults, - user_id=user_id, + **{ + "queue_type": queue_type, + "entity_type": entity_type, + "exclude_defaults": exclude_defaults, + "only_defaults": only_defaults, + "user_id": user_id, + } ) return self.get( "/api/v2/moderation/action_config", @@ -119,7 +121,7 @@ def bulk_delete_action_config( def delete_action_config( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteActionConfigResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -162,6 +164,7 @@ def analyze( async_response: Optional[bool] = None, config_key: Optional[str] = None, content_published_at: Optional[datetime] = None, + country_code: Optional[str] = None, entity_creator_id: Optional[str] = None, entity_id: Optional[str] = None, entity_type: Optional[str] = None, @@ -175,6 +178,7 @@ def analyze( async_response=async_response, config_key=config_key, content_published_at=content_published_at, + country_code=country_code, entity_creator_id=entity_creator_id, entity_id=entity_id, entity_type=entity_type, @@ -192,6 +196,7 @@ def appeal( appeal_reason: str, entity_id: str, entity_type: str, + channel_cid: Optional[str] = None, review_queue_item_id: Optional[str] = None, user_id: Optional[str] = None, attachments: Optional[List[str]] = None, @@ -201,6 +206,7 @@ def appeal( appeal_reason=appeal_reason, entity_id=entity_id, entity_type=entity_type, + channel_cid=channel_cid, review_queue_item_id=review_queue_item_id, user_id=user_id, attachments=attachments, @@ -273,9 +279,11 @@ def bulk_action_appeals( def ban( self, target_user_id: str, + ban_from_future_channels: Optional[bool] = None, banned_by_id: Optional[str] = None, channel_cid: Optional[str] = None, delete_messages: Optional[str] = None, + delete_reactions: Optional[bool] = None, ip_ban: Optional[bool] = None, reason: Optional[str] = None, shadow: Optional[bool] = None, @@ -284,9 +292,11 @@ def ban( ) -> StreamResponse[ModerationBanResponse]: json = BanRequest( target_user_id=target_user_id, + ban_from_future_channels=ban_from_future_channels, banned_by_id=banned_by_id, channel_cid=channel_cid, delete_messages=delete_messages, + delete_reactions=delete_reactions, ip_ban=ip_ban, reason=reason, shadow=shadow, @@ -322,6 +332,7 @@ def check( config_key: Optional[str] = None, config_team: Optional[str] = None, content_published_at: Optional[datetime] = None, + country_code: Optional[str] = None, test_mode: Optional[bool] = None, user_id: Optional[str] = None, config: Optional[ModerationConfig] = None, @@ -336,6 +347,7 @@ def check( config_key=config_key, config_team=config_team, content_published_at=content_published_at, + country_code=country_code, test_mode=test_mode, user_id=user_id, config=config, @@ -375,6 +387,7 @@ def upsert_config( bodyguard_config: Optional[AITextConfig] = None, flood_config: Optional[FloodConfig] = None, google_vision_config: Optional[GoogleVisionConfig] = None, + intent_config: Optional[IntentConfigRequest] = None, llm_config: Optional[LLMConfig] = None, rule_builder_config: Optional[RuleBuilderConfig] = None, user: Optional[UserRequest] = None, @@ -398,6 +411,7 @@ def upsert_config( bodyguard_config=bodyguard_config, flood_config=flood_config, google_vision_config=google_vision_config, + intent_config=intent_config, llm_config=llm_config, rule_builder_config=rule_builder_config, user=user, @@ -410,7 +424,7 @@ def upsert_config( def delete_config( self, key: str, team: Optional[str] = None, user_id: Optional[str] = None ) -> StreamResponse[DeleteModerationConfigResponse]: - query_params = build_query_param(team=team, user_id=user_id) + query_params = build_query_param(**{"team": team, "user_id": user_id}) path_params = { "key": key, } @@ -425,7 +439,7 @@ def delete_config( def get_config( self, key: str, team: Optional[str] = None ) -> StreamResponse[GetConfigResponse]: - query_params = build_query_param(team=team) + query_params = build_query_param(**{"team": team}) path_params = { "key": key, } @@ -676,7 +690,7 @@ def upsert_moderation_rule( def delete_moderation_rule( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[DeleteModerationRuleResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -808,7 +822,7 @@ def start_policy_test_run(self, id: str) -> StreamResponse[PolicyTestRunResponse def list_queues( self, user_id: Optional[str] = None ) -> StreamResponse[ListQueuesResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) return self.get( "/api/v2/moderation/queues", ListQueuesResponse, query_params=query_params ) @@ -839,7 +853,7 @@ def create_queue( def get_queue( self, id: str, user_id: Optional[str] = None ) -> StreamResponse[QueueResponse]: - query_params = build_query_param(user_id=user_id) + query_params = build_query_param(**{"user_id": user_id}) path_params = { "id": id, } @@ -973,6 +987,9 @@ def submit_action( delete_activity: Optional[DeleteActivityRequestPayload] = None, delete_comment: Optional[DeleteCommentRequestPayload] = None, delete_message: Optional[DeleteMessageRequestPayload] = None, + delete_poll: Optional[DeletePollRequestPayload] = None, + delete_poll_answer: Optional[DeletePollAnswerRequestPayload] = None, + delete_poll_option: Optional[DeletePollOptionRequestPayload] = None, delete_reaction: Optional[DeleteReactionRequestPayload] = None, delete_user: Optional[DeleteUserRequestPayload] = None, delete_user_messages: Optional[DeleteUserMessagesRequestPayload] = None, @@ -998,6 +1015,9 @@ def submit_action( delete_activity=delete_activity, delete_comment=delete_comment, delete_message=delete_message, + delete_poll=delete_poll, + delete_poll_answer=delete_poll_answer, + delete_poll_option=delete_poll_option, delete_reaction=delete_reaction, delete_user=delete_user, delete_user_messages=delete_user_messages, @@ -1055,9 +1075,11 @@ def unban( unbanned_by: Optional[UserRequest] = None, ) -> StreamResponse[UnbanResponse]: query_params = build_query_param( - target_user_id=target_user_id, - channel_cid=channel_cid, - created_by=created_by, + **{ + "target_user_id": target_user_id, + "channel_cid": channel_cid, + "created_by": created_by, + } ) json = UnbanRequest( unbanned_by_id=unbanned_by_id, unbanned_by=unbanned_by diff --git a/getstream/tests/test_webhook.py b/getstream/tests/test_webhook.py index 1f5c9f03..ad54bf6c 100644 --- a/getstream/tests/test_webhook.py +++ b/getstream/tests/test_webhook.py @@ -1,23 +1,25 @@ # Code generated by GetStream internal OpenAPI code generator. DO NOT EDIT. -import hashlib import hmac +import hashlib + import warnings + from pathlib import Path import pytest from getstream.webhook import ( - InvalidWebhookError, - UnknownEvent, get_event_type, - parse_event, - parse_sns, - parse_sqs, parse_webhook_event, - verify_and_parse_webhook, - verify_signature, verify_webhook_signature, + verify_signature, + parse_event, + verify_and_parse_webhook, + parse_sqs, + parse_sns, + UnknownEvent, + InvalidWebhookError, ) @@ -324,6 +326,12 @@ def test_parse_call_ring(self): event = parse_webhook_event({"type": "call.ring"}) assert type(event).__name__ == "CallRingEvent" + def test_parse_call_ring_settled(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "call.ring_settled"}) + assert type(event).__name__ == "CallRingSettledEvent" + def test_parse_call_rtmp_broadcast_failed(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -550,6 +558,18 @@ def test_parse_export_channels_success(self): event = parse_webhook_event({"type": "export.channels.success"}) assert type(event).__name__ == "AsyncExportChannelsEvent" + def test_parse_export_label_results_error(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "export.label_results.error"}) + assert type(event).__name__ == "AsyncExportErrorEvent" + + def test_parse_export_label_results_success(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "export.label_results.success"}) + assert type(event).__name__ == "AsyncExportLabelResultsEvent" + def test_parse_export_moderation_logs_error(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -700,6 +720,12 @@ def test_parse_feeds_comment_deleted(self): event = parse_webhook_event({"type": "feeds.comment.deleted"}) assert type(event).__name__ == "CommentDeletedEvent" + def test_parse_feeds_comment_feedback(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "feeds.comment.feedback"}) + assert type(event).__name__ == "CommentFeedbackEvent" + def test_parse_feeds_comment_reaction_added(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -820,6 +846,18 @@ def test_parse_flag_updated(self): event = parse_webhook_event({"type": "flag.updated"}) assert type(event).__name__ == "FlagUpdatedEvent" + def test_parse_import_error(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "import.error"}) + assert type(event).__name__ == "AsyncImportErrorEvent" + + def test_parse_import_success(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "import.success"}) + assert type(event).__name__ == "AsyncImportSuccessEvent" + def test_parse_ingress_error(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -970,6 +1008,42 @@ def test_parse_notification_thread_message_new(self): event = parse_webhook_event({"type": "notification.thread_message_new"}) assert type(event).__name__ == "NotificationThreadMessageNewEvent" + def test_parse_poll_closed(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.closed"}) + assert type(event).__name__ == "PollClosedEvent" + + def test_parse_poll_deleted(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.deleted"}) + assert type(event).__name__ == "PollDeletedEvent" + + def test_parse_poll_updated(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.updated"}) + assert type(event).__name__ == "PollUpdatedEvent" + + def test_parse_poll_vote_casted(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.vote_casted"}) + assert type(event).__name__ == "PollVoteCastedEvent" + + def test_parse_poll_vote_changed(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.vote_changed"}) + assert type(event).__name__ == "PollVoteChangedEvent" + + def test_parse_poll_vote_removed(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "poll.vote_removed"}) + assert type(event).__name__ == "PollVoteRemovedEvent" + def test_parse_reaction_deleted(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -1018,6 +1092,18 @@ def test_parse_review_queue_item_updated(self): event = parse_webhook_event({"type": "review_queue_item.updated"}) assert type(event).__name__ == "ReviewQueueItemUpdatedEvent" + def test_parse_scheduled_message_failed(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "scheduled_message.failed"}) + assert type(event).__name__ == "ScheduledMessageFailedEvent" + + def test_parse_scheduled_message_sent(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + event = parse_webhook_event({"type": "scheduled_message.sent"}) + assert type(event).__name__ == "ScheduledMessageSentEvent" + def test_parse_thread_updated(self): with warnings.catch_warnings(): warnings.simplefilter("ignore", RuntimeWarning) @@ -1135,7 +1221,7 @@ def test_invalid_json(self): # --------------------------------------------------------------------------- # Conformance fixture suite (CHA-2961). The fixture set is generated by -# chat/'s `chat-manager openapi generate-webhook-fixtures` subcommand; the +# chat/'s `build/openapi generate-webhook-fixtures` subcommand; the # repo's generate.sh copies it into tests/assets/webhooks/ at codegen time # (per stream-py AGENTS.md ยง8: test file assets live under tests/assets/). # --------------------------------------------------------------------------- diff --git a/getstream/video/async_call.py b/getstream/video/async_call.py index ec7fd972..47833f89 100644 --- a/getstream/video/async_call.py +++ b/getstream/video/async_call.py @@ -311,10 +311,17 @@ async def get_call_report( @attach_call_cid_async async def ring( - self, video: Optional[bool] = None, members_ids: Optional[List[str]] = None + self, + video: Optional[bool] = None, + members_ids: Optional[List[str]] = None, + custom: Optional[Dict[str, object]] = None, ) -> StreamResponse[RingCallResponse]: response = await self.client.ring_call( - type=self.call_type, id=self.id, video=video, members_ids=members_ids + type=self.call_type, + id=self.id, + video=video, + members_ids=members_ids, + custom=custom, ) self._sync_from_response(response.data) return response diff --git a/getstream/video/async_rest_client.py b/getstream/video/async_rest_client.py index 993e7317..b7daaa6a 100644 --- a/getstream/video/async_rest_client.py +++ b/getstream/video/async_rest_client.py @@ -49,7 +49,7 @@ async def query_user_feedback( sort: Optional[List[SortParamRequest]] = None, filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryUserFeedbackResponse]: - query_params = build_query_param(full=full) + query_params = build_query_param(**{"full": full}) json = QueryUserFeedbackRequest( limit=limit, next=next, @@ -88,6 +88,17 @@ async def query_call_members( "/api/v2/video/call/members", QueryCallMembersResponse, json=json ) + @telemetry.operation_name("getstream.api.video.query_call_ring_attempts") + async def query_call_ring_attempts( + self, id: str, type: str, call_session_id: Optional[str] = None + ) -> StreamResponse[QueryCallRingAttemptsResponse]: + json = QueryCallRingAttemptsRequest( + id=id, type=type, call_session_id=call_session_id + ).to_dict() + return await self.post( + "/api/v2/video/call/ring_attempts", QueryCallRingAttemptsResponse, json=json + ) + @telemetry.operation_name("getstream.api.video.query_call_stats") async def query_call_stats( self, @@ -119,7 +130,12 @@ async def get_call( video: Optional[bool] = None, ) -> StreamResponse[GetCallResponse]: query_params = build_query_param( - members_limit=members_limit, ring=ring, notify=notify, video=video + **{ + "members_limit": members_limit, + "ring": ring, + "notify": notify, + "video": video, + } ) path_params = { "type": type, @@ -443,7 +459,7 @@ async def query_call_participants( limit: Optional[int] = None, filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallParticipantsResponse]: - query_params = build_query_param(limit=limit) + query_params = build_query_param(**{"limit": limit}) path_params = { "id": id, "type": type, @@ -536,7 +552,7 @@ async def stop_recording( async def get_call_report( self, type: str, id: str, session_id: Optional[str] = None ) -> StreamResponse[GetCallReportResponse]: - query_params = build_query_param(session_id=session_id) + query_params = build_query_param(**{"session_id": session_id}) path_params = { "type": type, "id": id, @@ -555,12 +571,15 @@ async def ring_call( id: str, video: Optional[bool] = None, members_ids: Optional[List[str]] = None, + custom: Optional[Dict[str, object]] = None, ) -> StreamResponse[RingCallResponse]: path_params = { "type": type, "id": id, } - json = RingCallRequest(video=video, members_ids=members_ids).to_dict() + json = RingCallRequest( + video=video, members_ids=members_ids, custom=custom + ).to_dict() return await self.post( "/api/v2/video/call/{type}/{id}/ring", RingCallResponse, @@ -631,7 +650,7 @@ async def get_call_participant_session_metrics( since: Optional[datetime] = None, until: Optional[datetime] = None, ) -> StreamResponse[GetCallParticipantSessionMetricsResponse]: - query_params = build_query_param(since=since, until=until) + query_params = build_query_param(**{"since": since, "until": until}) path_params = { "type": type, "id": id, @@ -658,7 +677,12 @@ async def query_call_participant_sessions( filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallParticipantSessionsResponse]: query_params = build_query_param( - limit=limit, prev=prev, next=next, filter_conditions=filter_conditions + **{ + "limit": limit, + "prev": prev, + "next": next, + "filter_conditions": filter_conditions, + } ) path_params = { "type": type, @@ -1000,11 +1024,13 @@ async def get_call_stats_map( exclude_sfus: Optional[bool] = None, ) -> StreamResponse[QueryCallStatsMapResponse]: query_params = build_query_param( - start_time=start_time, - end_time=end_time, - exclude_publishers=exclude_publishers, - exclude_subscribers=exclude_subscribers, - exclude_sfus=exclude_sfus, + **{ + "start_time": start_time, + "end_time": end_time, + "exclude_publishers": exclude_publishers, + "exclude_subscribers": exclude_subscribers, + "exclude_sfus": exclude_sfus, + } ) path_params = { "call_type": call_type, @@ -1033,7 +1059,7 @@ async def get_call_session_participant_stats_details( max_points: Optional[int] = None, ) -> StreamResponse[GetCallSessionParticipantStatsDetailsResponse]: query_params = build_query_param( - since=since, until=until, max_points=max_points + **{"since": since, "until": until, "max_points": max_points} ) path_params = { "call_type": call_type, @@ -1064,11 +1090,13 @@ async def query_call_session_participant_stats( filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallSessionParticipantStatsResponse]: query_params = build_query_param( - limit=limit, - prev=prev, - next=next, - sort=sort, - filter_conditions=filter_conditions, + **{ + "limit": limit, + "prev": prev, + "next": next, + "sort": sort, + "filter_conditions": filter_conditions, + } ) path_params = { "call_type": call_type, @@ -1097,7 +1125,7 @@ async def get_call_session_participant_stats_timeline( severity: Optional[List[str]] = None, ) -> StreamResponse[QueryCallSessionParticipantStatsTimelineResponse]: query_params = build_query_param( - start_time=start_time, end_time=end_time, severity=severity + **{"start_time": start_time, "end_time": end_time, "severity": severity} ) path_params = { "call_type": call_type, @@ -1400,7 +1428,9 @@ async def query_aggregate_call_stats( async def get_daily_digest( self, date: Optional[str] = None, target_app_id: Optional[str] = None ) -> StreamResponse[GetDailyDigestResponse]: - query_params = build_query_param(date=date, target_app_id=target_app_id) + query_params = build_query_param( + **{"date": date, "target_app_id": target_app_id} + ) return await self.get( "/api/v2/video/stats/daily_digest", GetDailyDigestResponse, diff --git a/getstream/video/call.py b/getstream/video/call.py index 197f5d0e..a085a40e 100644 --- a/getstream/video/call.py +++ b/getstream/video/call.py @@ -305,10 +305,17 @@ def get_call_report( @attach_call_cid def ring( - self, video: Optional[bool] = None, members_ids: Optional[List[str]] = None + self, + video: Optional[bool] = None, + members_ids: Optional[List[str]] = None, + custom: Optional[Dict[str, object]] = None, ) -> StreamResponse[RingCallResponse]: response = self.client.ring_call( - type=self.call_type, id=self.id, video=video, members_ids=members_ids + type=self.call_type, + id=self.id, + video=video, + members_ids=members_ids, + custom=custom, ) self._sync_from_response(response.data) return response diff --git a/getstream/video/rest_client.py b/getstream/video/rest_client.py index 7d634b52..f19d4392 100644 --- a/getstream/video/rest_client.py +++ b/getstream/video/rest_client.py @@ -47,7 +47,7 @@ def query_user_feedback( sort: Optional[List[SortParamRequest]] = None, filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryUserFeedbackResponse]: - query_params = build_query_param(full=full) + query_params = build_query_param(**{"full": full}) json = QueryUserFeedbackRequest( limit=limit, next=next, @@ -86,6 +86,17 @@ def query_call_members( "/api/v2/video/call/members", QueryCallMembersResponse, json=json ) + @telemetry.operation_name("getstream.api.video.query_call_ring_attempts") + def query_call_ring_attempts( + self, id: str, type: str, call_session_id: Optional[str] = None + ) -> StreamResponse[QueryCallRingAttemptsResponse]: + json = QueryCallRingAttemptsRequest( + id=id, type=type, call_session_id=call_session_id + ).to_dict() + return self.post( + "/api/v2/video/call/ring_attempts", QueryCallRingAttemptsResponse, json=json + ) + @telemetry.operation_name("getstream.api.video.query_call_stats") def query_call_stats( self, @@ -115,7 +126,12 @@ def get_call( video: Optional[bool] = None, ) -> StreamResponse[GetCallResponse]: query_params = build_query_param( - members_limit=members_limit, ring=ring, notify=notify, video=video + **{ + "members_limit": members_limit, + "ring": ring, + "notify": notify, + "video": video, + } ) path_params = { "type": type, @@ -439,7 +455,7 @@ def query_call_participants( limit: Optional[int] = None, filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallParticipantsResponse]: - query_params = build_query_param(limit=limit) + query_params = build_query_param(**{"limit": limit}) path_params = { "id": id, "type": type, @@ -532,7 +548,7 @@ def stop_recording( def get_call_report( self, type: str, id: str, session_id: Optional[str] = None ) -> StreamResponse[GetCallReportResponse]: - query_params = build_query_param(session_id=session_id) + query_params = build_query_param(**{"session_id": session_id}) path_params = { "type": type, "id": id, @@ -551,12 +567,15 @@ def ring_call( id: str, video: Optional[bool] = None, members_ids: Optional[List[str]] = None, + custom: Optional[Dict[str, object]] = None, ) -> StreamResponse[RingCallResponse]: path_params = { "type": type, "id": id, } - json = RingCallRequest(video=video, members_ids=members_ids).to_dict() + json = RingCallRequest( + video=video, members_ids=members_ids, custom=custom + ).to_dict() return self.post( "/api/v2/video/call/{type}/{id}/ring", RingCallResponse, @@ -627,7 +646,7 @@ def get_call_participant_session_metrics( since: Optional[datetime] = None, until: Optional[datetime] = None, ) -> StreamResponse[GetCallParticipantSessionMetricsResponse]: - query_params = build_query_param(since=since, until=until) + query_params = build_query_param(**{"since": since, "until": until}) path_params = { "type": type, "id": id, @@ -654,7 +673,12 @@ def query_call_participant_sessions( filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallParticipantSessionsResponse]: query_params = build_query_param( - limit=limit, prev=prev, next=next, filter_conditions=filter_conditions + **{ + "limit": limit, + "prev": prev, + "next": next, + "filter_conditions": filter_conditions, + } ) path_params = { "type": type, @@ -996,11 +1020,13 @@ def get_call_stats_map( exclude_sfus: Optional[bool] = None, ) -> StreamResponse[QueryCallStatsMapResponse]: query_params = build_query_param( - start_time=start_time, - end_time=end_time, - exclude_publishers=exclude_publishers, - exclude_subscribers=exclude_subscribers, - exclude_sfus=exclude_sfus, + **{ + "start_time": start_time, + "end_time": end_time, + "exclude_publishers": exclude_publishers, + "exclude_subscribers": exclude_subscribers, + "exclude_sfus": exclude_sfus, + } ) path_params = { "call_type": call_type, @@ -1029,7 +1055,7 @@ def get_call_session_participant_stats_details( max_points: Optional[int] = None, ) -> StreamResponse[GetCallSessionParticipantStatsDetailsResponse]: query_params = build_query_param( - since=since, until=until, max_points=max_points + **{"since": since, "until": until, "max_points": max_points} ) path_params = { "call_type": call_type, @@ -1060,11 +1086,13 @@ def query_call_session_participant_stats( filter_conditions: Optional[Dict[str, object]] = None, ) -> StreamResponse[QueryCallSessionParticipantStatsResponse]: query_params = build_query_param( - limit=limit, - prev=prev, - next=next, - sort=sort, - filter_conditions=filter_conditions, + **{ + "limit": limit, + "prev": prev, + "next": next, + "sort": sort, + "filter_conditions": filter_conditions, + } ) path_params = { "call_type": call_type, @@ -1093,7 +1121,7 @@ def get_call_session_participant_stats_timeline( severity: Optional[List[str]] = None, ) -> StreamResponse[QueryCallSessionParticipantStatsTimelineResponse]: query_params = build_query_param( - start_time=start_time, end_time=end_time, severity=severity + **{"start_time": start_time, "end_time": end_time, "severity": severity} ) path_params = { "call_type": call_type, @@ -1392,7 +1420,9 @@ def query_aggregate_call_stats( def get_daily_digest( self, date: Optional[str] = None, target_app_id: Optional[str] = None ) -> StreamResponse[GetDailyDigestResponse]: - query_params = build_query_param(date=date, target_app_id=target_app_id) + query_params = build_query_param( + **{"date": date, "target_app_id": target_app_id} + ) return self.get( "/api/v2/video/stats/daily_digest", GetDailyDigestResponse, diff --git a/getstream/webhook.py b/getstream/webhook.py index b8c39083..8f61529b 100644 --- a/getstream/webhook.py +++ b/getstream/webhook.py @@ -2,43 +2,21 @@ import base64 import gzip -import hashlib import hmac +import hashlib import json import zlib from dataclasses import dataclass, field from datetime import datetime from typing import Any - from .models import ( - ActivityAddedEvent, - ActivityDeletedEvent, - ActivityFeedbackEvent, - ActivityMarkEvent, - ActivityPinnedEvent, - ActivityReactionAddedEvent, - ActivityReactionDeletedEvent, - ActivityReactionUpdatedEvent, - ActivityRemovedFromFeedEvent, - ActivityRestoredEvent, - ActivityUnpinnedEvent, - ActivityUpdatedEvent, + CustomEvent, AppealAcceptedEvent, AppealCreatedEvent, AppealRejectedEvent, - AsyncBulkImageModerationEvent, - AsyncExportChannelsEvent, - AsyncExportErrorEvent, - AsyncExportModerationLogsEvent, - AsyncExportReviewQueueEvent, - AsyncExportUsersEvent, - BlockedUserEvent, - BookmarkAddedEvent, - BookmarkDeletedEvent, - BookmarkFolderDeletedEvent, - BookmarkFolderUpdatedEvent, - BookmarkUpdatedEvent, CallAcceptedEvent, + BlockedUserEvent, + ClosedCaptionEvent, CallClosedCaptionsFailedEvent, CallClosedCaptionsStartedEvent, CallClosedCaptionsStoppedEvent, @@ -53,6 +31,7 @@ CallHLSBroadcastingFailedEvent, CallHLSBroadcastingStartedEvent, CallHLSBroadcastingStoppedEvent, + KickedUserEvent, CallLiveStartedEvent, CallMemberAddedEvent, CallMemberRemovedEvent, @@ -62,6 +41,8 @@ CallModerationBlurEvent, CallModerationWarningEvent, CallNotificationEvent, + PermissionRequestEvent, + UpdatedCallPermissionsEvent, CallReactionEvent, CallRecordingFailedEvent, CallRecordingReadyEvent, @@ -69,6 +50,7 @@ CallRecordingStoppedEvent, CallRejectedEvent, CallRingEvent, + CallRingSettledEvent, CallRtmpBroadcastFailedEvent, CallRtmpBroadcastStartedEvent, CallRtmpBroadcastStoppedEvent, @@ -82,96 +64,121 @@ CallTranscriptionReadyEvent, CallTranscriptionStartedEvent, CallTranscriptionStoppedEvent, + UnblockedUserEvent, CallUpdatedEvent, CallUserFeedbackSubmittedEvent, CallUserMutedEvent, CampaignCompletedEvent, CampaignStartedEvent, - ChannelBatchCompletedEvent, - ChannelBatchStartedEvent, ChannelCreatedEvent, ChannelDeletedEvent, ChannelFrozenEvent, ChannelHiddenEvent, + MaxStreakChangedEvent, ChannelMutedEvent, ChannelTruncatedEvent, ChannelUnFrozenEvent, ChannelUnmutedEvent, ChannelUpdatedEvent, ChannelVisibleEvent, - ClosedCaptionEvent, + ChannelBatchCompletedEvent, + ChannelBatchStartedEvent, + CustomVideoEvent, + AsyncBulkImageModerationEvent, + AsyncExportChannelsEvent, + AsyncExportLabelResultsEvent, + AsyncExportModerationLogsEvent, + AsyncExportReviewQueueEvent, + AsyncExportErrorEvent, + AsyncExportUsersEvent, + ActivityAddedEvent, + ActivityDeletedEvent, + ActivityFeedbackEvent, + ActivityMarkEvent, + ActivityPinnedEvent, + ActivityReactionAddedEvent, + ActivityReactionDeletedEvent, + ActivityReactionUpdatedEvent, + ActivityRemovedFromFeedEvent, + ActivityRestoredEvent, + ActivityUnpinnedEvent, + ActivityUpdatedEvent, + BookmarkAddedEvent, + BookmarkDeletedEvent, + BookmarkUpdatedEvent, + BookmarkFolderDeletedEvent, + BookmarkFolderUpdatedEvent, CommentAddedEvent, CommentDeletedEvent, + CommentFeedbackEvent, CommentReactionAddedEvent, CommentReactionDeletedEvent, CommentReactionUpdatedEvent, CommentRestoredEvent, CommentUpdatedEvent, - CustomEvent, - CustomVideoEvent, FeedCreatedEvent, FeedDeletedEvent, + FeedUpdatedEvent, FeedGroupChangedEvent, FeedGroupDeletedEvent, FeedGroupRestoredEvent, FeedMemberAddedEvent, FeedMemberRemovedEvent, FeedMemberUpdatedEvent, - FeedUpdatedEvent, - FlagUpdatedEvent, FollowCreatedEvent, FollowDeletedEvent, FollowUpdatedEvent, + NotificationFeedUpdatedEvent, + StoriesFeedUpdatedEvent, + FlagUpdatedEvent, + AsyncImportErrorEvent, + AsyncImportSuccessEvent, IngressErrorEvent, IngressStartedEvent, IngressStoppedEvent, - KickedUserEvent, - MaxStreakChangedEvent, MemberAddedEvent, MemberRemovedEvent, MemberUpdatedEvent, MessageDeletedEvent, MessageFlaggedEvent, MessageNewEvent, + PendingMessageEvent, MessageReadEvent, MessageUnblockedEvent, MessageUndeletedEvent, MessageUpdatedEvent, ModerationAnalysisFailedEvent, - ModerationCheckCompletedEvent, ModerationCustomActionEvent, ModerationFlaggedEvent, ModerationImageAnalysisCompleteEvent, ModerationMarkReviewedEvent, - ModerationRulesTriggeredEvent, ModerationTextAnalysisCompleteEvent, - NotificationFeedUpdatedEvent, + ModerationCheckCompletedEvent, + ModerationRulesTriggeredEvent, NotificationMarkUnreadEvent, + ReminderNotificationEvent, NotificationThreadMessageNewEvent, - PendingMessageEvent, - PermissionRequestEvent, + PollClosedEvent, + PollDeletedEvent, + PollUpdatedEvent, + PollVoteCastedEvent, + PollVoteChangedEvent, + PollVoteRemovedEvent, ReactionDeletedEvent, ReactionNewEvent, ReactionUpdatedEvent, ReminderCreatedEvent, ReminderDeletedEvent, - ReminderNotificationEvent, ReminderUpdatedEvent, ReviewQueueItemNewEvent, ReviewQueueItemUpdatedEvent, - StoriesFeedUpdatedEvent, + ScheduledMessageFailedEvent, + ScheduledMessageSentEvent, ThreadUpdatedEvent, - UnblockedUserEvent, - UpdatedCallPermissionsEvent, UserBannedEvent, UserDeactivatedEvent, UserDeletedEvent, UserFlaggedEvent, - UserGroupCreatedEvent, - UserGroupDeletedEvent, - UserGroupMemberAddedEvent, - UserGroupMemberRemovedEvent, - UserGroupUpdatedEvent, UserMessagesDeletedEvent, UserMutedEvent, UserReactivatedEvent, @@ -179,8 +186,14 @@ UserUnmutedEvent, UserUnreadReminderEvent, UserUpdatedEvent, + UserGroupCreatedEvent, + UserGroupDeletedEvent, + UserGroupMemberAddedEvent, + UserGroupMemberRemovedEvent, + UserGroupUpdatedEvent, ) + # Webhook event type constants EVENT_TYPE_WILDCARD = "*" EVENT_TYPE_APPEAL_ACCEPTED = "appeal.accepted" @@ -222,6 +235,7 @@ EVENT_TYPE_CALL_RECORDING_STOPPED = "call.recording_stopped" EVENT_TYPE_CALL_REJECTED = "call.rejected" EVENT_TYPE_CALL_RING = "call.ring" +EVENT_TYPE_CALL_RING_SETTLED = "call.ring_settled" EVENT_TYPE_CALL_RTMP_BROADCAST_FAILED = "call.rtmp_broadcast_failed" EVENT_TYPE_CALL_RTMP_BROADCAST_STARTED = "call.rtmp_broadcast_started" EVENT_TYPE_CALL_RTMP_BROADCAST_STOPPED = "call.rtmp_broadcast_stopped" @@ -261,6 +275,8 @@ EVENT_TYPE_EXPORT_BULK_IMAGE_MODERATION_SUCCESS = "export.bulk_image_moderation.success" EVENT_TYPE_EXPORT_CHANNELS_ERROR = "export.channels.error" EVENT_TYPE_EXPORT_CHANNELS_SUCCESS = "export.channels.success" +EVENT_TYPE_EXPORT_LABEL_RESULTS_ERROR = "export.label_results.error" +EVENT_TYPE_EXPORT_LABEL_RESULTS_SUCCESS = "export.label_results.success" EVENT_TYPE_EXPORT_MODERATION_LOGS_ERROR = "export.moderation_logs.error" EVENT_TYPE_EXPORT_MODERATION_LOGS_SUCCESS = "export.moderation_logs.success" EVENT_TYPE_EXPORT_REVIEW_QUEUE_ERROR = "export.review_queue.error" @@ -286,6 +302,7 @@ EVENT_TYPE_FEEDS_BOOKMARK_FOLDER_UPDATED = "feeds.bookmark_folder.updated" EVENT_TYPE_FEEDS_COMMENT_ADDED = "feeds.comment.added" EVENT_TYPE_FEEDS_COMMENT_DELETED = "feeds.comment.deleted" +EVENT_TYPE_FEEDS_COMMENT_FEEDBACK = "feeds.comment.feedback" EVENT_TYPE_FEEDS_COMMENT_REACTION_ADDED = "feeds.comment.reaction.added" EVENT_TYPE_FEEDS_COMMENT_REACTION_DELETED = "feeds.comment.reaction.deleted" EVENT_TYPE_FEEDS_COMMENT_REACTION_UPDATED = "feeds.comment.reaction.updated" @@ -306,6 +323,8 @@ EVENT_TYPE_FEEDS_NOTIFICATION_FEED_UPDATED = "feeds.notification_feed.updated" EVENT_TYPE_FEEDS_STORIES_FEED_UPDATED = "feeds.stories_feed.updated" EVENT_TYPE_FLAG_UPDATED = "flag.updated" +EVENT_TYPE_IMPORT_ERROR = "import.error" +EVENT_TYPE_IMPORT_SUCCESS = "import.success" EVENT_TYPE_INGRESS_ERROR = "ingress.error" EVENT_TYPE_INGRESS_STARTED = "ingress.started" EVENT_TYPE_INGRESS_STOPPED = "ingress.stopped" @@ -331,6 +350,12 @@ EVENT_TYPE_NOTIFICATION_MARK_UNREAD = "notification.mark_unread" EVENT_TYPE_NOTIFICATION_REMINDER_DUE = "notification.reminder_due" EVENT_TYPE_NOTIFICATION_THREAD_MESSAGE_NEW = "notification.thread_message_new" +EVENT_TYPE_POLL_CLOSED = "poll.closed" +EVENT_TYPE_POLL_DELETED = "poll.deleted" +EVENT_TYPE_POLL_UPDATED = "poll.updated" +EVENT_TYPE_POLL_VOTE_CASTED = "poll.vote_casted" +EVENT_TYPE_POLL_VOTE_CHANGED = "poll.vote_changed" +EVENT_TYPE_POLL_VOTE_REMOVED = "poll.vote_removed" EVENT_TYPE_REACTION_DELETED = "reaction.deleted" EVENT_TYPE_REACTION_NEW = "reaction.new" EVENT_TYPE_REACTION_UPDATED = "reaction.updated" @@ -339,6 +364,8 @@ EVENT_TYPE_REMINDER_UPDATED = "reminder.updated" EVENT_TYPE_REVIEW_QUEUE_ITEM_NEW = "review_queue_item.new" EVENT_TYPE_REVIEW_QUEUE_ITEM_UPDATED = "review_queue_item.updated" +EVENT_TYPE_SCHEDULED_MESSAGE_FAILED = "scheduled_message.failed" +EVENT_TYPE_SCHEDULED_MESSAGE_SENT = "scheduled_message.sent" EVENT_TYPE_THREAD_UPDATED = "thread.updated" EVENT_TYPE_USER_BANNED = "user.banned" EVENT_TYPE_USER_DEACTIVATED = "user.deactivated" @@ -492,6 +519,7 @@ def _get_event_class(event_type: str): "call.recording_stopped": CallRecordingStoppedEvent, "call.rejected": CallRejectedEvent, "call.ring": CallRingEvent, + "call.ring_settled": CallRingSettledEvent, "call.rtmp_broadcast_failed": CallRtmpBroadcastFailedEvent, "call.rtmp_broadcast_started": CallRtmpBroadcastStartedEvent, "call.rtmp_broadcast_stopped": CallRtmpBroadcastStoppedEvent, @@ -529,6 +557,8 @@ def _get_event_class(event_type: str): "export.bulk_image_moderation.success": AsyncBulkImageModerationEvent, "export.channels.error": AsyncExportErrorEvent, "export.channels.success": AsyncExportChannelsEvent, + "export.label_results.error": AsyncExportErrorEvent, + "export.label_results.success": AsyncExportLabelResultsEvent, "export.moderation_logs.error": AsyncExportErrorEvent, "export.moderation_logs.success": AsyncExportModerationLogsEvent, "export.review_queue.error": AsyncExportErrorEvent, @@ -554,6 +584,7 @@ def _get_event_class(event_type: str): "feeds.bookmark_folder.updated": BookmarkFolderUpdatedEvent, "feeds.comment.added": CommentAddedEvent, "feeds.comment.deleted": CommentDeletedEvent, + "feeds.comment.feedback": CommentFeedbackEvent, "feeds.comment.reaction.added": CommentReactionAddedEvent, "feeds.comment.reaction.deleted": CommentReactionDeletedEvent, "feeds.comment.reaction.updated": CommentReactionUpdatedEvent, @@ -574,6 +605,8 @@ def _get_event_class(event_type: str): "feeds.notification_feed.updated": NotificationFeedUpdatedEvent, "feeds.stories_feed.updated": StoriesFeedUpdatedEvent, "flag.updated": FlagUpdatedEvent, + "import.error": AsyncImportErrorEvent, + "import.success": AsyncImportSuccessEvent, "ingress.error": IngressErrorEvent, "ingress.started": IngressStartedEvent, "ingress.stopped": IngressStoppedEvent, @@ -599,6 +632,12 @@ def _get_event_class(event_type: str): "notification.mark_unread": NotificationMarkUnreadEvent, "notification.reminder_due": ReminderNotificationEvent, "notification.thread_message_new": NotificationThreadMessageNewEvent, + "poll.closed": PollClosedEvent, + "poll.deleted": PollDeletedEvent, + "poll.updated": PollUpdatedEvent, + "poll.vote_casted": PollVoteCastedEvent, + "poll.vote_changed": PollVoteChangedEvent, + "poll.vote_removed": PollVoteRemovedEvent, "reaction.deleted": ReactionDeletedEvent, "reaction.new": ReactionNewEvent, "reaction.updated": ReactionUpdatedEvent, @@ -607,6 +646,8 @@ def _get_event_class(event_type: str): "reminder.updated": ReminderUpdatedEvent, "review_queue_item.new": ReviewQueueItemNewEvent, "review_queue_item.updated": ReviewQueueItemUpdatedEvent, + "scheduled_message.failed": ScheduledMessageFailedEvent, + "scheduled_message.sent": ScheduledMessageSentEvent, "thread.updated": ThreadUpdatedEvent, "user.banned": UserBannedEvent, "user.deactivated": UserDeactivatedEvent, diff --git a/tests/test_chat_scheduled_messages.py b/tests/test_chat_scheduled_messages.py new file mode 100644 index 00000000..11e16337 --- /dev/null +++ b/tests/test_chat_scheduled_messages.py @@ -0,0 +1,96 @@ +import datetime + +import pytest + +from getstream import Stream +from getstream.chat.channel import Channel +from getstream.exceptions import StreamApiException +from getstream.models import MessageRequest, ScheduledMessagesFilter + + +pytestmark = pytest.mark.integration + +# Error code the API returns when scheduled messages are off for the app. +SCHEDULED_MESSAGES_DISABLED = 114 + + +def in_one_hour() -> datetime.datetime: + # Millisecond precision, so it survives the round trip through the API. + now = datetime.datetime.now(datetime.timezone.utc) + return (now + datetime.timedelta(hours=1)).replace( + microsecond=now.microsecond // 1000 * 1000 + ) + + +def channel_cid(channel: Channel) -> str: + return f"{channel.channel_type}:{channel.channel_id}" + + +def schedule_message(channel: Channel, user_id: str, text: str) -> str: + """Schedule a message an hour ahead and return its scheduled message ID. + + Skips the test when the app cannot schedule: the feature is opt-in per + app, and backends without it ignore scheduled_at and send right away. + """ + try: + response = channel.send_message( + message=MessageRequest(text=text, user_id=user_id), + scheduled_at=in_one_hour(), + ) + except StreamApiException as e: + if e.code == SCHEDULED_MESSAGES_DISABLED: + pytest.skip("Scheduled messages are not enabled for this app") + raise + if response.data.scheduled_message is None: + pytest.skip("This backend does not support scheduled messages") + return response.data.scheduled_message.id + + +class TestScheduledMessages: + def test_schedule_and_query(self, client: Stream, channel: Channel, random_user): + scheduled_id = schedule_message(channel, random_user.id, "scheduled hello") + + response = client.chat.query_scheduled_messages( + user_id=random_user.id, + filter=ScheduledMessagesFilter(cid=channel_cid(channel)), + ) + + assert [m.id for m in response.data.scheduled_messages] == [scheduled_id] + scheduled = response.data.scheduled_messages[0] + assert scheduled.status == "scheduled" + assert scheduled.cid == channel_cid(channel) + assert scheduled.message.text == "scheduled hello" + assert isinstance(scheduled.scheduled_at, datetime.datetime) + + def test_update(self, client: Stream, channel: Channel, random_user): + scheduled_id = schedule_message(channel, random_user.id, "scheduled hello") + scheduled_at = in_one_hour() + + response = client.chat.update_scheduled_message( + id=scheduled_id, + scheduled_at=scheduled_at, + message=MessageRequest(text="rescheduled hello"), + ) + + assert response.data.scheduled_message.message.text == "rescheduled hello" + assert response.data.scheduled_message.scheduled_at == scheduled_at + + def test_send_now(self, client: Stream, channel: Channel, random_user): + scheduled_id = schedule_message(channel, random_user.id, "scheduled hello") + + response = client.chat.send_scheduled_message_now(id=scheduled_id) + + assert response.data.scheduled_message.id == scheduled_id + + def test_delete(self, client: Stream, channel: Channel, random_user): + scheduled_id = schedule_message(channel, random_user.id, "scheduled hello") + + response = client.chat.delete_scheduled_message(id=scheduled_id) + + assert response.data.scheduled_message.id == scheduled_id + assert response.data.scheduled_message.status == "canceled" + query = client.chat.query_scheduled_messages( + user_id=random_user.id, + filter=ScheduledMessagesFilter(cid=channel_cid(channel)), + ) + assert scheduled_id not in [m.id for m in query.data.scheduled_messages]