getstream 7.2.0__tar.gz → 8.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {getstream-7.2.0 → getstream-8.1.0}/.release-please-manifest.json +1 -1
- {getstream-7.2.0 → getstream-8.1.0}/CHANGELOG.md +33 -3
- getstream-8.1.0/MIGRATION_v7_to_v8.md +114 -0
- {getstream-7.2.0 → getstream-8.1.0}/PKG-INFO +1 -1
- {getstream-7.2.0 → getstream-8.1.0}/getstream/base.py +19 -13
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/async_channel.py +96 -1
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/async_client.py +3 -37
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/async_rest_client.py +367 -88
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/channel.py +96 -1
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/client.py +3 -37
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/rest_client.py +365 -88
- getstream-8.1.0/getstream/common/async_client.py +12 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/common/async_rest_client.py +138 -44
- getstream-8.1.0/getstream/common/client.py +12 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/common/rest_client.py +134 -44
- {getstream-7.2.0 → getstream-8.1.0}/getstream/feeds/feeds.py +23 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/feeds/rest_client.py +335 -67
- {getstream-7.2.0 → getstream-8.1.0}/getstream/models/__init__.py +10641 -5477
- {getstream-7.2.0 → getstream-8.1.0}/getstream/moderation/async_rest_client.py +291 -23
- {getstream-7.2.0 → getstream-8.1.0}/getstream/moderation/rest_client.py +291 -23
- {getstream-7.2.0 → getstream-8.1.0}/getstream/tests/test_webhook.py +126 -10
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/async_call.py +9 -2
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/async_rest_client.py +50 -20
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/call.py +9 -2
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rest_client.py +50 -20
- {getstream-7.2.0 → getstream-8.1.0}/getstream/webhook.py +114 -57
- {getstream-7.2.0 → getstream-8.1.0}/pyproject.toml +1 -1
- {getstream-7.2.0 → getstream-8.1.0}/uv.lock +1 -1
- getstream-7.2.0/getstream/common/async_client.py +0 -58
- getstream-7.2.0/getstream/common/client.py +0 -58
- {getstream-7.2.0 → getstream-8.1.0}/.cursor/worktrees.json +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.env.example +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/actions/build-rtc-wheel/action.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/actions/python-uv-setup/action.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/ci.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/pr_title.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/release.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/rtc_wheels.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/run_integration.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/run_tests.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/scheduled_test.yml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.github/workflows/stream-py.code-workspace +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.gitignore +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.gitmodules +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/.pre-commit-config.yaml +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/AGENTS.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/DEVELOPMENT.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/LICENSE.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/MIGRATION_v2_to_v3.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/MIGRATION_v6_to_v7.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/Makefile +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/README.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/dev.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/01-setup-and-auth.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/02-users.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/03-channels.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/04-messages-and-reactions.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/05-moderation.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/06-devices.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/docs/migration-from-stream-chat-python/README.md +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/generate.sh +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/generate_webrtc.sh +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/chat/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/common/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/common/telemetry.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/config.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/exceptions.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/feeds/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/feeds/client.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/generic.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/logging_utils.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/meta.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/moderation/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/moderation/async_client.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/moderation/client.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/rate_limit.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/stream.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/stream_response.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/tasks.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/utils/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/utils/event_emitter.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/version.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/async_client.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/client.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/openai.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/audio_forwarder.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/audio_track.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/connection_manager.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/g711.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/participants.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/__init__.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.pyi +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/track_util.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/tracks.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/utils.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/getstream/video/rtc/video_forwarder.py +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/pytest.ini +0 -0
- {getstream-7.2.0 → getstream-8.1.0}/release-please-config.json +0 -0
|
@@ -7,17 +7,47 @@ Entries from 6.1.1 on are generated by release-please from commit messages; earl
|
|
|
7
7
|
ones were written by hand in the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
8
8
|
format.
|
|
9
9
|
|
|
10
|
-
## [
|
|
10
|
+
## [8.1.0](https://github.com/GetStream/stream-py/compare/v8.0.0...v8.1.0) (2026-11-12)
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
### Features
|
|
14
14
|
|
|
15
|
-
* **moderation:** add `
|
|
15
|
+
* **moderation:** add `count_review_queue` to count the review queue items that match a filter ([6fce09c](https://github.com/GetStream/stream-py/commit/6fce09c8c38dc555c9a5de2bd701a312cc177a7e))
|
|
16
|
+
* **moderation:** add `expected_value` to `UserCustomPropertyParameters`, `CallCustomPropertyParameters`, `ContentCustomPropertyParameters` and `ContentCustomPropertyCountParameters`, so value-operator custom-property rules can be set ([6fce09c](https://github.com/GetStream/stream-py/commit/6fce09c8c38dc555c9a5de2bd701a312cc177a7e))
|
|
17
|
+
* **moderation:** add agent review: `submit_agent_feedback`, `list_agent_feedback` and `get_agent_review_status`, plus `agent_review` on `update_app` and `agent_recommendation` on `submit_action` ([6fce09c](https://github.com/GetStream/stream-py/commit/6fce09c8c38dc555c9a5de2bd701a312cc177a7e))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* cap decompressed gzip webhook bodies at 10 MiB (`getstream.webhook.MAX_WEBHOOK_PAYLOAD_BYTES`) and reject data after the gzip stream, because decompression runs before the signature check ([6fce09c](https://github.com/GetStream/stream-py/commit/6fce09c8c38dc555c9a5de2bd701a312cc177a7e))
|
|
23
|
+
|
|
24
|
+
## [8.0.0](https://github.com/GetStream/stream-py/compare/v7.1.0...v8.0.0) (2026-11-11)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### ⚠ BREAKING CHANGES
|
|
28
|
+
|
|
29
|
+
* new optional parameters sit in the middle of many method signatures, and a few models and return types changed. See [#313](https://github.com/GetStream/stream-py/pull/313) and the [migration guide](https://github.com/GetStream/stream-py/blob/main/MIGRATION_v7_to_v8.md) for all changes. The main changes:
|
|
30
|
+
* pass optional parameters by keyword: 41 methods, among them `send_message`, `query_channels`, `get_or_create_channel`, `ban`, `unban`, `get_activity` and the poll methods, have new parameters before existing ones
|
|
31
|
+
* `update_channel_type` makes `automod`, `automod_behavior` and `max_message_length` optional, and `submit_moderation_feedback` makes `message`, `published_at` and `reference` optional; both move them behind the required parameters
|
|
32
|
+
* `update_campaign` returns `UpdateCampaignResponse` and `stop_campaign` returns `StopCampaignResponse`; read the campaign from `.campaign`
|
|
33
|
+
* the empty request models `CancelImportV2TaskRequest`, `CommitMessageRequest`, `EndCallRequest`, `ExportFeedUserDataRequest`, `RestoreFeedGroupRequest`, `StartHLSBroadcastingRequest`, `StartPolicyTestRunRequest`, `StopAllRTMPBroadcastsRequest`, `StopCampaignRequest`, `StopFrameRecordingRequest`, `StopHLSBroadcastingRequest`, `VideoEndCallRequestPayload` and `VideoKickUserRequestPayload` are removed, as is `Time`; `EnrichedReaction.created_at` and `updated_at` are `str`
|
|
34
|
+
* `AIVideoConfig.provider` is removed
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* add `fork_channel`, `fork_message_ids` and `rewind_channel`, also on `Channel` as `fork`, `fork_message_ids` and `rewind` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
39
|
+
* add `query_channel_bans`, `search_all`, `claim_guest_user`, `check_gcp_pub_sub` and the history window team methods `get_history_window_teams`, `set_history_window_teams` and `delete_history_window_teams` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
40
|
+
* add `query_deleted_channels` to list deleted channels ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
41
|
+
* add scheduled messages: `send_message(scheduled_at=...)`, `query_scheduled_messages`, `update_scheduled_message`, `delete_scheduled_message` and `send_scheduled_message_now` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
42
|
+
* **feeds:** add feed exports (`export_feeds_app`, `export_feeds_by_type`, `cancel_export_feeds`), retention policies (`feeds_get_retention_policy`, `feeds_set_retention_policy`, `feeds_delete_retention_policy`, `feeds_get_retention_policy_runs`), user interests (`upsert_user_interests`, `delete_user_interests`), `comment_feedback` and `get_notification_status` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
43
|
+
* **moderation:** add config versions (`save_config_version`, `list_config_versions`, `get_config_version`, `publish_config_version`), `query_moderation_feedback` and `reveal_media`, plus `matched_blocklists` on `ModerationRulesTriggeredEvent` and `severities` on `ClosedCaptionRuleParameters` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
44
|
+
* page through `list_imports` with `limit` and `offset`, and set `ImportV2TaskSettings.custom_format` to read custom data from the root of each item or from a nested `custom` object ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
45
|
+
* regenerate the client from the current Stream API ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
46
|
+
* **video:** add `query_call_ring_attempts` ([cc5538e](https://github.com/GetStream/stream-py/commit/cc5538ec188fc7a683c82da7959eaa18358990e7))
|
|
16
47
|
|
|
17
48
|
|
|
18
49
|
### Bug Fixes
|
|
19
50
|
|
|
20
|
-
* cap decompressed gzip webhook bodies at 10 MiB (`getstream.webhook.MAX_WEBHOOK_PAYLOAD_BYTES`) and reject data after the gzip stream, because decompression runs before the signature check ([138e50a](https://github.com/GetStream/stream-py/commit/138e50ad2a713a85eef61508d6cf854d64692460))
|
|
21
51
|
* stop double-encoding list query parameters ([#306](https://github.com/GetStream/stream-py/issues/306)) ([a584118](https://github.com/GetStream/stream-py/commit/a584118a15db9bd60b7daf571fa13d52e459b6d2)), closes [#305](https://github.com/GetStream/stream-py/issues/305)
|
|
22
52
|
|
|
23
53
|
## [7.1.0](https://github.com/GetStream/stream-py/compare/v7.0.0...v7.1.0) (2026-11-04)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Migration Guide: v7 → v8
|
|
2
|
+
|
|
3
|
+
v8 regenerates the client from the current Stream API. Most of the changes add new methods, models and optional parameters. This guide lists the changes that can break code written for stream-py 7.1.0.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install "getstream==8.*"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Pass optional parameters by keyword
|
|
10
|
+
|
|
11
|
+
Generated method parameters are positional-or-keyword, and v8 adds new optional parameters in the middle of many signatures. A call that passes optional arguments by position can now send a value to the wrong parameter. Calls that pass them by keyword work unchanged.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
# v7: the fourth positional argument was `user_id`
|
|
15
|
+
client.feeds.get_activity(activity_id, "first", 10, "jane")
|
|
16
|
+
|
|
17
|
+
# v8: "jane" would go to the new `skip_own_followings`, so pass it by keyword
|
|
18
|
+
client.feeds.get_activity(activity_id, comment_sort="first", comment_limit=10, user_id="jane")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
These methods have new parameters before existing ones. Each async client has the same changes.
|
|
22
|
+
|
|
23
|
+
| Method | New parameters | First moved parameter |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `Channel.get_or_create`, `chat.get_or_create_channel`, `chat.get_or_create_distinct_channel` | `include_scheduled_messages` | `state` |
|
|
26
|
+
| `Channel.send_message`, `chat.send_message` | `scheduled_at` | `skip_enrich_url` |
|
|
27
|
+
| `chat.query_channels` | `include_active_member_count` | `limit` |
|
|
28
|
+
| `chat.grouped_query_channels` | `sort` | `groups` |
|
|
29
|
+
| `chat.channel_batch_update` | `synchronous` | `custom_unset` |
|
|
30
|
+
| `chat.create_channel_type` | `quotes`, `reminders`, `automod_thresholds` | `reactions` |
|
|
31
|
+
| `chat.create_reminder`, `chat.update_reminder` | `expires_at` | `remind_at` |
|
|
32
|
+
| `chat.cast_poll_vote`, `feeds.cast_poll_vote` | `config_key`, `config_team` | `user_id` |
|
|
33
|
+
| `create_poll` | `config_key`, `config_team`, `team` | `description` |
|
|
34
|
+
| `update_poll` | `config_key`, `config_team` | `description` |
|
|
35
|
+
| `update_poll_partial`, `create_poll_option`, `update_poll_option` | `config_key`, `config_team` | `user_id` |
|
|
36
|
+
| `update_app` | `two_way_block_effect_enabled` | `user_response_time_enabled` |
|
|
37
|
+
| `Feed.get_or_create`, `feeds.get_or_create_feed` | `discard_actors` | `data` |
|
|
38
|
+
| `Feed.query_pinned_activities`, `feeds.query_pinned_activities` | `include_expired_activities`, `include_moderated_activities`, `include_soft_deleted_activities`, `user_id` | `limit` |
|
|
39
|
+
| `feeds.add_activity` | `skip_existing` | `skip_push` |
|
|
40
|
+
| `feeds.get_activity` | `skip_own_followings` | `user_id` |
|
|
41
|
+
| `feeds.query_activities` | `include_moderated_activities` | `include_private_activities` |
|
|
42
|
+
| `feeds.query_comments` | `include_soft_deleted_comments` | `limit` |
|
|
43
|
+
| `feeds.query_follows` | `include_group_counts` | `limit` |
|
|
44
|
+
| `feeds.follow_batch`, `feeds.get_or_create_follows` | `_async` | `create_users` |
|
|
45
|
+
| `feeds.unfollow_batch`, `feeds.get_or_create_unfollows` | `_async` | `delete_notification_activity` |
|
|
46
|
+
| `moderation.analyze` | `country_code`, `country_codes` | `entity_creator_id` |
|
|
47
|
+
| `moderation.check` | `country_code`, `country_codes` | `test_mode` |
|
|
48
|
+
| `moderation.appeal` | `channel_cid` | `review_queue_item_id` |
|
|
49
|
+
| `moderation.ban` | `ban_from_future_channels`, `delete_reactions` | `banned_by_id` |
|
|
50
|
+
| `moderation.unban` | `remove_future_channels_ban` | `unbanned_by_id` |
|
|
51
|
+
| `moderation.submit_action` | `delete_poll`, `delete_poll_answer`, `delete_poll_option` | `delete_reaction` |
|
|
52
|
+
| `moderation.upsert_config` | `engine`, `intent_config` | `flood_config` |
|
|
53
|
+
|
|
54
|
+
## Parameters that are now optional
|
|
55
|
+
|
|
56
|
+
These parameters are now optional, so they moved behind the required ones. Pass them by keyword.
|
|
57
|
+
|
|
58
|
+
- `chat.update_channel_type`: `automod`, `automod_behavior` and `max_message_length`. `max_message_length` now comes after `mark_messages_pending`.
|
|
59
|
+
- `moderation.submit_moderation_feedback`: `message`, `published_at` and `reference`. They now come after `channel_id` and the new `confidence`, `entity_creator_id`, `entity_id`, `entity_type`, `false_positive` and `feedback_type` parameters, and before the new `review_queue_item_id` and `user_id`.
|
|
60
|
+
|
|
61
|
+
The matching request models, `UpdateChannelTypeRequest` and `SubmitModerationFeedbackRequest`, give these fields a `None` default, so they also moved in the dataclass field order.
|
|
62
|
+
|
|
63
|
+
## Campaign responses
|
|
64
|
+
|
|
65
|
+
`update_campaign` returns `UpdateCampaignResponse` and `stop_campaign` returns `StopCampaignResponse`. v7 decoded both into `CampaignResponse`, whose fields came back as `None` because the API returns the campaign inside a `campaign` field. Read the campaign from that field.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# v7
|
|
69
|
+
resp = client.chat.stop_campaign(id=campaign_id)
|
|
70
|
+
status = resp.data.status # None
|
|
71
|
+
|
|
72
|
+
# v8
|
|
73
|
+
resp = client.chat.stop_campaign(id=campaign_id)
|
|
74
|
+
status = resp.data.campaign.status
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Removed models
|
|
78
|
+
|
|
79
|
+
These request models had no fields, because their endpoints take no request body. The methods that call those endpoints keep their signatures.
|
|
80
|
+
|
|
81
|
+
- `CancelImportV2TaskRequest`
|
|
82
|
+
- `CommitMessageRequest`
|
|
83
|
+
- `EndCallRequest`
|
|
84
|
+
- `ExportFeedUserDataRequest`
|
|
85
|
+
- `RestoreFeedGroupRequest`
|
|
86
|
+
- `StartHLSBroadcastingRequest`
|
|
87
|
+
- `StartPolicyTestRunRequest`
|
|
88
|
+
- `StopAllRTMPBroadcastsRequest`
|
|
89
|
+
- `StopCampaignRequest`
|
|
90
|
+
- `StopFrameRecordingRequest`
|
|
91
|
+
- `StopHLSBroadcastingRequest`
|
|
92
|
+
- `VideoEndCallRequestPayload`
|
|
93
|
+
- `VideoKickUserRequestPayload`
|
|
94
|
+
|
|
95
|
+
`Time` was also removed. `EnrichedReaction.created_at` and `EnrichedReaction.updated_at` are now `Optional[str]`.
|
|
96
|
+
|
|
97
|
+
## Removed field
|
|
98
|
+
|
|
99
|
+
`AIVideoConfig.provider` was removed. Drop it from `AIVideoConfig(...)` calls in your moderation config.
|
|
100
|
+
|
|
101
|
+
## Type annotation changes
|
|
102
|
+
|
|
103
|
+
These changes affect only type checkers. Runtime values are unchanged.
|
|
104
|
+
|
|
105
|
+
- `own_capabilities` on call, channel and feed responses is annotated as `List[str]`. The constants classes `OwnCapability`, `ChannelOwnCapability` and `FeedOwnCapability` still exist, and their values are strings.
|
|
106
|
+
- `CampaignResponse.sender_mode` and `CampaignResponse.sender_visibility` are now `Optional[str]`.
|
|
107
|
+
|
|
108
|
+
## Required response fields
|
|
109
|
+
|
|
110
|
+
`ChannelMemberResponse.user_id` and `OwnUserResponse.blocked_user_ids` are now required and have no default. Responses the SDK decodes are unaffected, but code that builds these models itself, such as test fixtures, must pass both fields, or the constructor raises `TypeError` and a direct `from_dict` without `infer_missing=True` raises `KeyError`.
|
|
111
|
+
|
|
112
|
+
## Uploads
|
|
113
|
+
|
|
114
|
+
`upload_file`, `upload_image`, `upload_channel_file` and `upload_channel_image` keep their signatures and still send `multipart/form-data`. `file` is a keyword with a `None` default in the signature, but the upload raises `ValueError` without it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: getstream
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.1.0
|
|
4
4
|
Summary: GetStream Python SDK - Build scalable activity feeds, chat, and video calling applications
|
|
5
5
|
Author-email: sachaarbonel <sacha.arbonel@hotmail.fr>, tbarbugli <tbarbugli@gmail.com>
|
|
6
6
|
License-File: LICENSE.md
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
import uuid
|
|
8
8
|
import warnings
|
|
9
9
|
import asyncio
|
|
10
|
-
from typing import Any, Dict,
|
|
10
|
+
from typing import Any, Dict, Optional, Tuple, Type, cast, get_origin
|
|
11
11
|
|
|
12
12
|
from getstream.exceptions import (
|
|
13
13
|
StreamApiException,
|
|
@@ -143,6 +143,20 @@ def _read_file_bytes(file_path: str) -> bytes:
|
|
|
143
143
|
return f.read()
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
def _multipart_form(body: Dict[str, Any]) -> Tuple[str, Dict[str, str]]:
|
|
147
|
+
"""Split an upload body into the file path and the other set fields as form
|
|
148
|
+
values: strings as-is, anything else JSON-encoded, which is how the API reads
|
|
149
|
+
"user" and "upload_sizes"."""
|
|
150
|
+
fields = _strip_none(body)
|
|
151
|
+
file_path = fields.pop("file", None)
|
|
152
|
+
if not file_path:
|
|
153
|
+
raise ValueError("file path must be provided")
|
|
154
|
+
return file_path, {
|
|
155
|
+
name: value if isinstance(value, str) else json.dumps(value)
|
|
156
|
+
for name, value in fields.items()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
146
160
|
def _strip_none(obj):
|
|
147
161
|
"""Recursively remove None values from dicts so unset optional fields
|
|
148
162
|
are omitted from the JSON body instead of being sent as null. ``NULL``
|
|
@@ -569,23 +583,19 @@ class BaseClient(TelemetryEndpointMixin, BaseConfig, ResponseParserMixin, ABC):
|
|
|
569
583
|
self,
|
|
570
584
|
path: str,
|
|
571
585
|
data_type: Type[T],
|
|
572
|
-
file_path: str,
|
|
573
586
|
*,
|
|
574
587
|
path_params: Optional[Dict[str, str]] = None,
|
|
575
588
|
query_params: Optional[Dict[str, str]] = None,
|
|
576
|
-
|
|
589
|
+
json: Dict[str, Any],
|
|
577
590
|
) -> StreamResponse[T]:
|
|
578
591
|
"""Send a multipart/form-data upload request, matching Go/PHP SDK behavior."""
|
|
592
|
+
file_path, data = _multipart_form(json)
|
|
579
593
|
file_name = os.path.basename(file_path)
|
|
580
594
|
content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
|
|
581
595
|
with open(file_path, "rb") as f:
|
|
582
596
|
file_content = f.read()
|
|
583
597
|
|
|
584
598
|
files = {"file": (file_name, file_content, content_type)}
|
|
585
|
-
data: Dict[str, str] = {}
|
|
586
|
-
for field_name, field_value in form_fields or []:
|
|
587
|
-
data[field_name] = field_value
|
|
588
|
-
|
|
589
599
|
kwargs: Dict[str, Any] = {"files": files}
|
|
590
600
|
if data:
|
|
591
601
|
kwargs["data"] = data
|
|
@@ -700,23 +710,19 @@ class AsyncBaseClient(TelemetryEndpointMixin, BaseConfig, ResponseParserMixin, A
|
|
|
700
710
|
self,
|
|
701
711
|
path: str,
|
|
702
712
|
data_type: Type[T],
|
|
703
|
-
file_path: str,
|
|
704
713
|
*,
|
|
705
714
|
path_params: Optional[Dict[str, str]] = None,
|
|
706
715
|
query_params: Optional[Dict[str, str]] = None,
|
|
707
|
-
|
|
716
|
+
json: Dict[str, Any],
|
|
708
717
|
) -> StreamResponse[T]:
|
|
709
718
|
"""Send a multipart/form-data upload request, matching Go/PHP SDK behavior."""
|
|
719
|
+
file_path, data = _multipart_form(json)
|
|
710
720
|
file_name = os.path.basename(file_path)
|
|
711
721
|
content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
|
|
712
722
|
|
|
713
723
|
file_content = await asyncio.to_thread(_read_file_bytes, file_path)
|
|
714
724
|
|
|
715
725
|
files = {"file": (file_name, file_content, content_type)}
|
|
716
|
-
data: Dict[str, str] = {}
|
|
717
|
-
for field_name, field_value in form_fields or []:
|
|
718
|
-
data[field_name] = field_value
|
|
719
|
-
|
|
720
726
|
kwargs: Dict[str, Any] = {"files": files}
|
|
721
727
|
if data:
|
|
722
728
|
kwargs["data"] = data
|
|
@@ -18,7 +18,12 @@ class Channel:
|
|
|
18
18
|
self.custom_data = custom_data or {}
|
|
19
19
|
|
|
20
20
|
def _sync_from_response(self, data):
|
|
21
|
-
|
|
21
|
+
# A fork's response carries the new channel, not this one.
|
|
22
|
+
if (
|
|
23
|
+
hasattr(data, "channel")
|
|
24
|
+
and isinstance(data.channel, ChannelResponse)
|
|
25
|
+
and data.channel.cid == f"{self.channel_type}:{self.channel_id}"
|
|
26
|
+
):
|
|
22
27
|
self.custom_data = data.channel.custom
|
|
23
28
|
|
|
24
29
|
@attach_channel_cid_async
|
|
@@ -131,6 +136,33 @@ class Channel:
|
|
|
131
136
|
self._sync_from_response(response.data)
|
|
132
137
|
return response
|
|
133
138
|
|
|
139
|
+
@attach_channel_cid_async
|
|
140
|
+
async def query_channel_bans(
|
|
141
|
+
self,
|
|
142
|
+
exclude_expired_bans: Optional[bool] = None,
|
|
143
|
+
include_total: Optional[bool] = None,
|
|
144
|
+
limit: Optional[int] = None,
|
|
145
|
+
next: Optional[str] = None,
|
|
146
|
+
prev: Optional[str] = None,
|
|
147
|
+
user_id: Optional[str] = None,
|
|
148
|
+
filter_conditions: Optional[Dict[str, object]] = None,
|
|
149
|
+
user: Optional[UserRequest] = None,
|
|
150
|
+
) -> StreamResponse[QueryChannelBansResponse]:
|
|
151
|
+
response = await self.client.query_channel_bans(
|
|
152
|
+
type=self.channel_type,
|
|
153
|
+
id=self.channel_id,
|
|
154
|
+
exclude_expired_bans=exclude_expired_bans,
|
|
155
|
+
include_total=include_total,
|
|
156
|
+
limit=limit,
|
|
157
|
+
next=next,
|
|
158
|
+
prev=prev,
|
|
159
|
+
user_id=user_id,
|
|
160
|
+
filter_conditions=filter_conditions,
|
|
161
|
+
user=user,
|
|
162
|
+
)
|
|
163
|
+
self._sync_from_response(response.data)
|
|
164
|
+
return response
|
|
165
|
+
|
|
134
166
|
@attach_channel_cid_async
|
|
135
167
|
async def delete_draft(
|
|
136
168
|
self, parent_id: Optional[str] = None, user_id: Optional[str] = None
|
|
@@ -185,6 +217,44 @@ class Channel:
|
|
|
185
217
|
self._sync_from_response(response.data)
|
|
186
218
|
return response
|
|
187
219
|
|
|
220
|
+
@attach_channel_cid_async
|
|
221
|
+
async def fork(
|
|
222
|
+
self,
|
|
223
|
+
message_id: str,
|
|
224
|
+
channel: ForkChannelTarget,
|
|
225
|
+
exclude_attachments: Optional[bool] = None,
|
|
226
|
+
user_id: Optional[str] = None,
|
|
227
|
+
user: Optional[UserRequest] = None,
|
|
228
|
+
) -> StreamResponse[ForkChannelResponse]:
|
|
229
|
+
response = await self.client.fork_channel(
|
|
230
|
+
type=self.channel_type,
|
|
231
|
+
id=self.channel_id,
|
|
232
|
+
message_id=message_id,
|
|
233
|
+
channel=channel,
|
|
234
|
+
exclude_attachments=exclude_attachments,
|
|
235
|
+
user_id=user_id,
|
|
236
|
+
user=user,
|
|
237
|
+
)
|
|
238
|
+
self._sync_from_response(response.data)
|
|
239
|
+
return response
|
|
240
|
+
|
|
241
|
+
@attach_channel_cid_async
|
|
242
|
+
async def fork_message_ids(
|
|
243
|
+
self,
|
|
244
|
+
source_message_ids: List[str],
|
|
245
|
+
user_id: Optional[str] = None,
|
|
246
|
+
user: Optional[UserRequest] = None,
|
|
247
|
+
) -> StreamResponse[ForkMessageIDsResponse]:
|
|
248
|
+
response = await self.client.fork_message_ids(
|
|
249
|
+
type=self.channel_type,
|
|
250
|
+
id=self.channel_id,
|
|
251
|
+
source_message_ids=source_message_ids,
|
|
252
|
+
user_id=user_id,
|
|
253
|
+
user=user,
|
|
254
|
+
)
|
|
255
|
+
self._sync_from_response(response.data)
|
|
256
|
+
return response
|
|
257
|
+
|
|
188
258
|
@attach_channel_cid_async
|
|
189
259
|
async def hide(
|
|
190
260
|
self,
|
|
@@ -255,6 +325,7 @@ class Channel:
|
|
|
255
325
|
include_mentioned_members: Optional[bool] = None,
|
|
256
326
|
keep_channel_hidden: Optional[bool] = None,
|
|
257
327
|
pending: Optional[bool] = None,
|
|
328
|
+
scheduled_at: Optional[datetime] = None,
|
|
258
329
|
skip_enrich_url: Optional[bool] = None,
|
|
259
330
|
skip_push: Optional[bool] = None,
|
|
260
331
|
pending_message_metadata: Optional[Dict[str, str]] = None,
|
|
@@ -268,6 +339,7 @@ class Channel:
|
|
|
268
339
|
include_mentioned_members=include_mentioned_members,
|
|
269
340
|
keep_channel_hidden=keep_channel_hidden,
|
|
270
341
|
pending=pending,
|
|
342
|
+
scheduled_at=scheduled_at,
|
|
271
343
|
skip_enrich_url=skip_enrich_url,
|
|
272
344
|
skip_push=skip_push,
|
|
273
345
|
pending_message_metadata=pending_message_metadata,
|
|
@@ -333,6 +405,7 @@ class Channel:
|
|
|
333
405
|
async def get_or_create(
|
|
334
406
|
self,
|
|
335
407
|
hide_for_creator: Optional[bool] = None,
|
|
408
|
+
include_scheduled_messages: Optional[bool] = None,
|
|
336
409
|
state: Optional[bool] = None,
|
|
337
410
|
thread_unread_counts: Optional[bool] = None,
|
|
338
411
|
member_custom_include: Optional[List[str]] = None,
|
|
@@ -345,6 +418,7 @@ class Channel:
|
|
|
345
418
|
type=self.channel_type,
|
|
346
419
|
id=self.channel_id,
|
|
347
420
|
hide_for_creator=hide_for_creator,
|
|
421
|
+
include_scheduled_messages=include_scheduled_messages,
|
|
348
422
|
state=state,
|
|
349
423
|
thread_unread_counts=thread_unread_counts,
|
|
350
424
|
member_custom_include=member_custom_include,
|
|
@@ -375,6 +449,27 @@ class Channel:
|
|
|
375
449
|
self._sync_from_response(response.data)
|
|
376
450
|
return response
|
|
377
451
|
|
|
452
|
+
@attach_channel_cid_async
|
|
453
|
+
async def rewind(
|
|
454
|
+
self,
|
|
455
|
+
expected_last_message_id: str,
|
|
456
|
+
message_id: str,
|
|
457
|
+
inclusive: Optional[bool] = None,
|
|
458
|
+
user_id: Optional[str] = None,
|
|
459
|
+
user: Optional[UserRequest] = None,
|
|
460
|
+
) -> StreamResponse[RewindChannelResponse]:
|
|
461
|
+
response = await self.client.rewind_channel(
|
|
462
|
+
type=self.channel_type,
|
|
463
|
+
id=self.channel_id,
|
|
464
|
+
expected_last_message_id=expected_last_message_id,
|
|
465
|
+
message_id=message_id,
|
|
466
|
+
inclusive=inclusive,
|
|
467
|
+
user_id=user_id,
|
|
468
|
+
user=user,
|
|
469
|
+
)
|
|
470
|
+
self._sync_from_response(response.data)
|
|
471
|
+
return response
|
|
472
|
+
|
|
378
473
|
@attach_channel_cid_async
|
|
379
474
|
async def show(
|
|
380
475
|
self, user_id: Optional[str] = None, user: Optional[UserRequest] = None
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import json
|
|
2
1
|
from typing import List, Optional
|
|
3
2
|
|
|
4
3
|
from getstream.chat.async_channel import Channel
|
|
5
4
|
from getstream.chat.async_rest_client import ChatRestClient
|
|
6
|
-
from getstream.common import telemetry
|
|
7
5
|
from getstream.models import (
|
|
8
6
|
ImageSize,
|
|
9
7
|
OnlyUserID,
|
|
10
|
-
UploadChannelFileResponse,
|
|
11
8
|
UploadChannelResponse,
|
|
12
9
|
)
|
|
13
10
|
from getstream.stream_response import StreamResponse
|
|
@@ -27,26 +24,7 @@ class ChatClient(ChatRestClient):
|
|
|
27
24
|
def channel(self, call_type: str, id: str) -> Channel:
|
|
28
25
|
return Channel(self, call_type, id)
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
async def upload_channel_file(
|
|
32
|
-
self,
|
|
33
|
-
type: str,
|
|
34
|
-
id: str,
|
|
35
|
-
file: str,
|
|
36
|
-
user: Optional[OnlyUserID] = None,
|
|
37
|
-
) -> StreamResponse[UploadChannelFileResponse]:
|
|
38
|
-
form_fields = []
|
|
39
|
-
if user is not None:
|
|
40
|
-
form_fields.append(("user", json.dumps(user.to_dict())))
|
|
41
|
-
return await self._upload_multipart(
|
|
42
|
-
"/api/v2/chat/channels/{type}/{id}/file",
|
|
43
|
-
UploadChannelFileResponse,
|
|
44
|
-
file,
|
|
45
|
-
path_params={"type": type, "id": id},
|
|
46
|
-
form_fields=form_fields,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
@telemetry.operation_name("getstream.api.chat.upload_channel_image")
|
|
27
|
+
# Keeps the `channel_type` name this method has always taken; the generated one takes `type`.
|
|
50
28
|
async def upload_channel_image(
|
|
51
29
|
self,
|
|
52
30
|
channel_type: Optional[str] = None,
|
|
@@ -56,7 +34,6 @@ class ChatClient(ChatRestClient):
|
|
|
56
34
|
user: Optional[OnlyUserID] = None,
|
|
57
35
|
**kwargs,
|
|
58
36
|
) -> StreamResponse[UploadChannelResponse]:
|
|
59
|
-
# Backward compatibility for generated wrappers passing `type=...`.
|
|
60
37
|
if channel_type is None:
|
|
61
38
|
channel_type = kwargs.pop("type", None)
|
|
62
39
|
if kwargs:
|
|
@@ -69,17 +46,6 @@ class ChatClient(ChatRestClient):
|
|
|
69
46
|
raise TypeError("upload_channel_image() missing required argument: 'id'")
|
|
70
47
|
if file is None:
|
|
71
48
|
raise TypeError("upload_channel_image() missing required argument: 'file'")
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
form_fields.append(("user", json.dumps(user.to_dict())))
|
|
75
|
-
if upload_sizes is not None:
|
|
76
|
-
form_fields.append(
|
|
77
|
-
("upload_sizes", json.dumps([s.to_dict() for s in upload_sizes]))
|
|
78
|
-
)
|
|
79
|
-
return await self._upload_multipart(
|
|
80
|
-
"/api/v2/chat/channels/{type}/{id}/image",
|
|
81
|
-
UploadChannelResponse,
|
|
82
|
-
file,
|
|
83
|
-
path_params={"type": channel_type, "id": id},
|
|
84
|
-
form_fields=form_fields,
|
|
49
|
+
return await super().upload_channel_image(
|
|
50
|
+
channel_type, id, file=file, upload_sizes=upload_sizes, user=user
|
|
85
51
|
)
|