stream-chat 4.11.0__py3-none-any.whl → 4.12.1__py3-none-any.whl

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.
stream_chat/__pkg__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  __author__ = "Tommaso Barbugli"
2
2
  __copyright__ = "Copyright 2019-2022, Stream.io, Inc"
3
- __version__ = "4.11.0"
3
+ __version__ = "4.12.1"
4
4
  __maintainer__ = "Tommaso Barbugli"
5
5
  __email__ = "support@getstream.io"
6
6
  __status__ = "Production"
@@ -0,0 +1,52 @@
1
+ import datetime
2
+ from typing import Any, Optional, Union
3
+
4
+ from stream_chat.base.campaign import CampaignInterface
5
+ from stream_chat.types.campaign import CampaignData
6
+ from stream_chat.types.stream_response import StreamResponse
7
+
8
+
9
+ class Campaign(CampaignInterface):
10
+ async def create(
11
+ self, campaign_id: Optional[str] = None, data: Optional[CampaignData] = None
12
+ ) -> StreamResponse:
13
+ if campaign_id is not None:
14
+ self.campaign_id = campaign_id
15
+ if data is not None:
16
+ self.data = data
17
+ state = await self.client.create_campaign( # type: ignore
18
+ campaign_id=self.campaign_id, data=self.data
19
+ )
20
+
21
+ if self.campaign_id is None and state.is_ok() and "campaign" in state:
22
+ self.campaign_id = state["campaign"]["id"]
23
+ return state
24
+
25
+ async def get(self) -> StreamResponse:
26
+ return await self.client.get_campaign( # type: ignore
27
+ campaign_id=self.campaign_id
28
+ )
29
+
30
+ async def update(self, data: CampaignData) -> StreamResponse:
31
+ return await self.client.update_campaign( # type: ignore
32
+ campaign_id=self.campaign_id, data=data
33
+ )
34
+
35
+ async def delete(self, **options: Any) -> StreamResponse:
36
+ return await self.client.delete_campaign( # type: ignore
37
+ campaign_id=self.campaign_id, **options
38
+ )
39
+
40
+ async def start(
41
+ self,
42
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
43
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
44
+ ) -> StreamResponse:
45
+ return await self.client.start_campaign( # type: ignore
46
+ campaign_id=self.campaign_id, scheduled_for=scheduled_for, stop_at=stop_at
47
+ )
48
+
49
+ async def stop(self) -> StreamResponse:
50
+ return await self.client.stop_campaign( # type: ignore
51
+ campaign_id=self.campaign_id
52
+ )
@@ -13,9 +13,21 @@ from typing import (
13
13
  Optional,
14
14
  Type,
15
15
  Union,
16
+ cast,
16
17
  )
17
18
  from urllib.parse import urlparse
18
19
 
20
+ from stream_chat.async_chat.campaign import Campaign
21
+ from stream_chat.async_chat.segment import Segment
22
+ from stream_chat.types.base import SortParam
23
+ from stream_chat.types.campaign import CampaignData, QueryCampaignsOptions
24
+ from stream_chat.types.segment import (
25
+ QuerySegmentsOptions,
26
+ QuerySegmentTargetsOptions,
27
+ SegmentData,
28
+ SegmentType,
29
+ )
30
+
19
31
  if sys.version_info >= (3, 8):
20
32
  from typing import Literal
21
33
  else:
@@ -473,7 +485,7 @@ class StreamChatAsync(StreamChatInterface, AsyncContextManager):
473
485
  return await self._parse_response(response)
474
486
 
475
487
  async def create_blocklist(
476
- self, name: str, words: Iterable[str], type: str = "regular"
488
+ self, name: str, words: Iterable[str], type: str = "word"
477
489
  ) -> StreamResponse:
478
490
  return await self.post(
479
491
  "blocklists", data={"name": name, "words": words, "type": type}
@@ -537,45 +549,148 @@ class StreamChatAsync(StreamChatInterface, AsyncContextManager):
537
549
  async def list_roles(self) -> StreamResponse:
538
550
  return await self.get("roles")
539
551
 
540
- async def create_segment(self, segment: Dict) -> StreamResponse:
541
- return await self.post("segments", data={"segment": segment})
552
+ def segment( # type: ignore
553
+ self,
554
+ segment_type: SegmentType,
555
+ segment_id: Optional[str] = None,
556
+ data: Optional[SegmentData] = None,
557
+ ) -> Segment:
558
+ return Segment(
559
+ client=self, segment_type=segment_type, segment_id=segment_id, data=data
560
+ )
561
+
562
+ async def create_segment(
563
+ self,
564
+ segment_type: SegmentType,
565
+ segment_id: Optional[str] = None,
566
+ data: Optional[SegmentData] = None,
567
+ ) -> StreamResponse:
568
+ payload = {"type": segment_type.value}
569
+ if segment_id is not None:
570
+ payload["id"] = segment_id
571
+ if data is not None:
572
+ payload.update(cast(dict, data))
573
+ return await self.post("segments", data=payload)
542
574
 
543
- async def query_segments(self, **params: Any) -> StreamResponse:
544
- return await self.get("segments", params={"payload": json.dumps(params)})
575
+ async def get_segment(self, segment_id: str) -> StreamResponse:
576
+ return await self.get(f"segments/{segment_id}")
545
577
 
546
- async def update_segment(self, segment_id: str, data: Dict) -> StreamResponse:
547
- return await self.put(f"segments/{segment_id}", data={"segment": data})
578
+ async def query_segments(
579
+ self,
580
+ filter_conditions: Optional[Dict[str, Any]] = None,
581
+ sort: Optional[List[SortParam]] = None,
582
+ options: Optional[QuerySegmentsOptions] = None,
583
+ ) -> StreamResponse:
584
+ payload = {}
585
+ if filter_conditions is not None:
586
+ payload["filter"] = filter_conditions
587
+ if sort is not None:
588
+ payload["sort"] = sort # type: ignore
589
+ if options is not None:
590
+ payload.update(cast(dict, options))
591
+ return await self.post("segments/query", data=payload)
592
+
593
+ async def update_segment(
594
+ self, segment_id: str, data: SegmentData
595
+ ) -> StreamResponse:
596
+ return await self.put(f"segments/{segment_id}", data=data)
548
597
 
549
598
  async def delete_segment(self, segment_id: str) -> StreamResponse:
550
599
  return await self.delete(f"segments/{segment_id}")
551
600
 
552
- async def create_campaign(self, campaign: Dict) -> StreamResponse:
553
- return await self.post("campaigns", data={"campaign": campaign})
601
+ async def segment_target_exists(
602
+ self, segment_id: str, target_id: str
603
+ ) -> StreamResponse:
604
+ return await self.get(f"segments/{segment_id}/target/{target_id}")
554
605
 
555
- async def query_campaigns(self, **params: Any) -> StreamResponse:
556
- return await self.get("campaigns", params={"payload": json.dumps(params)})
606
+ async def add_segment_targets(
607
+ self, segment_id: str, target_ids: List[str]
608
+ ) -> StreamResponse:
609
+ return await self.post(
610
+ f"segments/{segment_id}/addtargets", data={"target_ids": target_ids}
611
+ )
557
612
 
558
- async def update_campaign(self, campaign_id: str, data: Dict) -> StreamResponse:
559
- return await self.put(f"campaigns/{campaign_id}", data={"campaign": data})
613
+ async def query_segment_targets(
614
+ self,
615
+ segment_id: str,
616
+ filter_conditions: Optional[Dict[str, Any]] = None,
617
+ sort: Optional[List[SortParam]] = None,
618
+ options: Optional[QuerySegmentTargetsOptions] = None,
619
+ ) -> StreamResponse:
620
+ payload = {}
621
+ if filter_conditions is not None:
622
+ payload["filter"] = filter_conditions
623
+ if sort is not None:
624
+ payload["sort"] = sort # type: ignore
625
+ if options is not None:
626
+ payload.update(cast(dict, options))
627
+ return await self.post(f"segments/{segment_id}/targets/query", data=payload)
628
+
629
+ async def remove_segment_targets(
630
+ self, segment_id: str, target_ids: List[str]
631
+ ) -> StreamResponse:
632
+ return await self.post(
633
+ f"segments/{segment_id}/deletetargets", data={"target_ids": target_ids}
634
+ )
560
635
 
561
- async def delete_campaign(self, campaign_id: str, **options: Any) -> StreamResponse:
562
- return await self.delete(f"campaigns/{campaign_id}", params=options)
636
+ def campaign( # type: ignore
637
+ self, campaign_id: Optional[str] = None, data: Optional[CampaignData] = None
638
+ ) -> Campaign:
639
+ return Campaign(client=self, campaign_id=campaign_id, data=data)
563
640
 
564
- async def schedule_campaign(
565
- self, campaign_id: str, scheduled_for: int = None
641
+ async def create_campaign(
642
+ self, campaign_id: Optional[str] = None, data: Optional[CampaignData] = None
566
643
  ) -> StreamResponse:
567
- return await self.patch(
568
- f"campaigns/{campaign_id}/schedule", data={"scheduled_for": scheduled_for}
569
- )
644
+ payload = {"id": campaign_id}
645
+ if data is not None:
646
+ payload.update(cast(dict, data))
647
+ return await self.post("campaigns", data=payload)
570
648
 
571
- async def query_recipients(self, **params: Any) -> StreamResponse:
572
- return await self.get("recipients", params={"payload": json.dumps(params)})
649
+ async def get_campaign(self, campaign_id: str) -> StreamResponse:
650
+ return await self.get(f"campaigns/{campaign_id}")
573
651
 
574
- async def stop_campaign(self, campaign_id: str) -> StreamResponse:
575
- return await self.patch(f"campaigns/{campaign_id}/stop")
652
+ async def query_campaigns(
653
+ self,
654
+ filter_conditions: Optional[Dict[str, Any]] = None,
655
+ sort: Optional[List[SortParam]] = None,
656
+ options: QueryCampaignsOptions = None,
657
+ ) -> StreamResponse:
658
+ payload = {}
659
+ if filter_conditions is not None:
660
+ payload["filter"] = filter_conditions
661
+ if sort is not None:
662
+ payload["sort"] = sort # type: ignore
663
+ if options is not None:
664
+ payload.update(cast(dict, options))
665
+ return await self.post("campaigns/query", data=payload)
666
+
667
+ async def update_campaign(
668
+ self, campaign_id: str, data: CampaignData
669
+ ) -> StreamResponse:
670
+ return await self.put(f"campaigns/{campaign_id}", data=data)
671
+
672
+ async def delete_campaign(self, campaign_id: str, **options: Any) -> StreamResponse:
673
+ return await self.delete(f"campaigns/{campaign_id}", options)
576
674
 
577
- async def resume_campaign(self, campaign_id: str) -> StreamResponse:
578
- return await self.patch(f"campaigns/{campaign_id}/resume")
675
+ async def start_campaign(
676
+ self,
677
+ campaign_id: str,
678
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
679
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
680
+ ) -> StreamResponse:
681
+ payload = {}
682
+ if scheduled_for is not None:
683
+ if isinstance(scheduled_for, datetime.datetime):
684
+ scheduled_for = scheduled_for.isoformat()
685
+ payload["scheduled_for"] = scheduled_for
686
+ if stop_at is not None:
687
+ if isinstance(stop_at, datetime.datetime):
688
+ stop_at = stop_at.isoformat()
689
+ payload["stop_at"] = stop_at
690
+ return await self.post(f"campaigns/{campaign_id}/start", data=payload)
691
+
692
+ async def stop_campaign(self, campaign_id: str) -> StreamResponse:
693
+ return await self.post(f"campaigns/{campaign_id}/stop")
579
694
 
580
695
  async def test_campaign(
581
696
  self, campaign_id: str, users: Iterable[str]
@@ -0,0 +1,65 @@
1
+ from typing import Dict, List, Optional
2
+
3
+ from stream_chat.base.segment import SegmentInterface
4
+ from stream_chat.types.base import SortParam
5
+ from stream_chat.types.segment import QuerySegmentTargetsOptions, SegmentData
6
+ from stream_chat.types.stream_response import StreamResponse
7
+
8
+
9
+ class Segment(SegmentInterface):
10
+ async def create(
11
+ self, segment_id: Optional[str] = None, data: Optional[SegmentData] = None
12
+ ) -> StreamResponse:
13
+ if segment_id is not None:
14
+ self.segment_id = segment_id
15
+ if data is not None:
16
+ self.data = data
17
+
18
+ state = await self.client.create_segment( # type: ignore
19
+ segment_type=self.segment_type, segment_id=self.segment_id, data=self.data
20
+ )
21
+
22
+ if self.segment_id is None and state.is_ok() and "segment" in state:
23
+ self.segment_id = state["segment"]["id"]
24
+ return state
25
+
26
+ async def get(self) -> StreamResponse:
27
+ return await self.client.get_segment(segment_id=self.segment_id) # type: ignore
28
+
29
+ async def update(self, data: SegmentData) -> StreamResponse:
30
+ return await self.client.update_segment( # type: ignore
31
+ segment_id=self.segment_id, data=data
32
+ )
33
+
34
+ async def delete(self) -> StreamResponse:
35
+ return await self.client.delete_segment( # type: ignore
36
+ segment_id=self.segment_id
37
+ )
38
+
39
+ async def target_exists(self, target_id: str) -> StreamResponse:
40
+ return await self.client.segment_target_exists( # type: ignore
41
+ segment_id=self.segment_id, target_id=target_id
42
+ )
43
+
44
+ async def add_targets(self, target_ids: list) -> StreamResponse:
45
+ return await self.client.add_segment_targets( # type: ignore
46
+ segment_id=self.segment_id, target_ids=target_ids
47
+ )
48
+
49
+ async def query_targets(
50
+ self,
51
+ filter_conditions: Optional[Dict] = None,
52
+ sort: Optional[List[SortParam]] = None,
53
+ options: Optional[QuerySegmentTargetsOptions] = None,
54
+ ) -> StreamResponse:
55
+ return await self.client.query_segment_targets( # type: ignore
56
+ segment_id=self.segment_id,
57
+ filter_conditions=filter_conditions,
58
+ sort=sort,
59
+ options=options,
60
+ )
61
+
62
+ async def remove_targets(self, target_ids: list) -> StreamResponse:
63
+ return await self.client.remove_segment_targets( # type: ignore
64
+ segment_id=self.segment_id, target_ids=target_ids
65
+ )
@@ -0,0 +1,51 @@
1
+ import abc
2
+ import datetime
3
+ from typing import Awaitable, Optional, Union
4
+
5
+ from stream_chat.base.client import StreamChatInterface
6
+ from stream_chat.types.campaign import CampaignData
7
+ from stream_chat.types.stream_response import StreamResponse
8
+
9
+
10
+ class CampaignInterface(abc.ABC):
11
+ def __init__(
12
+ self,
13
+ client: StreamChatInterface,
14
+ campaign_id: Optional[str] = None,
15
+ data: CampaignData = None,
16
+ ):
17
+ self.client = client
18
+ self.campaign_id = campaign_id
19
+ self.data = data
20
+
21
+ @abc.abstractmethod
22
+ def create(
23
+ self, campaign_id: Optional[str], data: Optional[CampaignData]
24
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
25
+ pass
26
+
27
+ @abc.abstractmethod
28
+ def get(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
29
+ pass
30
+
31
+ @abc.abstractmethod
32
+ def update(
33
+ self, data: CampaignData
34
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
35
+ pass
36
+
37
+ @abc.abstractmethod
38
+ def delete(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
39
+ pass
40
+
41
+ @abc.abstractmethod
42
+ def start(
43
+ self,
44
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
45
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
46
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
47
+ pass
48
+
49
+ @abc.abstractmethod
50
+ def stop(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
51
+ pass
@@ -5,7 +5,16 @@ import hashlib
5
5
  import hmac
6
6
  import os
7
7
  import sys
8
- from typing import Any, Awaitable, Dict, Iterable, List, TypeVar, Union
8
+ from typing import Any, Awaitable, Dict, Iterable, List, Optional, TypeVar, Union
9
+
10
+ from stream_chat.types.base import SortParam
11
+ from stream_chat.types.campaign import CampaignData, QueryCampaignsOptions
12
+ from stream_chat.types.segment import (
13
+ QuerySegmentsOptions,
14
+ QuerySegmentTargetsOptions,
15
+ SegmentData,
16
+ SegmentType,
17
+ )
9
18
 
10
19
  if sys.version_info >= (3, 8):
11
20
  from typing import Literal
@@ -18,6 +27,10 @@ from stream_chat.types.stream_response import StreamResponse
18
27
 
19
28
  TChannel = TypeVar("TChannel")
20
29
 
30
+ TSegment = TypeVar("TSegment")
31
+
32
+ TCampaign = TypeVar("TCampaign")
33
+
21
34
 
22
35
  class StreamChatInterface(abc.ABC):
23
36
  def __init__(
@@ -585,7 +598,6 @@ class StreamChatInterface(abc.ABC):
585
598
  ) -> TChannel: # type: ignore[type-var]
586
599
  """
587
600
  Creates a channel object
588
-
589
601
  :param channel_type: the channel type
590
602
  :param channel_id: the id of the channel
591
603
  :param data: additional data, ie: {"members":[id1, id2, ...]}
@@ -753,7 +765,7 @@ class StreamChatInterface(abc.ABC):
753
765
 
754
766
  @abc.abstractmethod
755
767
  def create_blocklist(
756
- self, name: str, words: Iterable[str], type: str = "regular"
768
+ self, name: str, words: Iterable[str], type: str = "word"
757
769
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
758
770
  """
759
771
  Create a blocklist
@@ -918,18 +930,49 @@ class StreamChatInterface(abc.ABC):
918
930
  """
919
931
  pass
920
932
 
933
+ @abc.abstractmethod
934
+ def segment(
935
+ self,
936
+ segment_type: SegmentType,
937
+ segment_id: Optional[str],
938
+ data: Optional[SegmentData],
939
+ ) -> TSegment: # type: ignore[type-var]
940
+ """
941
+ Creates a channel object
942
+ :param segment_type: the segment type
943
+ :param segment_id: the id of the segment
944
+ :param data: the segment data, ie: {"members":[id1, id2, ...]}
945
+ :return: Segment
946
+ """
947
+ pass
948
+
921
949
  @abc.abstractmethod
922
950
  def create_segment(
923
- self, segment: Dict
951
+ self,
952
+ segment_type: SegmentType,
953
+ segment_id: Optional[str],
954
+ data: Optional[SegmentData] = None,
924
955
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
925
956
  """
926
957
  Create a segment
927
958
  """
928
959
  pass
929
960
 
961
+ @abc.abstractmethod
962
+ def get_segment(
963
+ self, segment_id: str
964
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
965
+ """
966
+ Query segments
967
+ """
968
+ pass
969
+
930
970
  @abc.abstractmethod
931
971
  def query_segments(
932
- self, **params: Any
972
+ self,
973
+ filter_conditions: Optional[Dict] = None,
974
+ sort: Optional[List[SortParam]] = None,
975
+ options: Optional[QuerySegmentsOptions] = None,
933
976
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
934
977
  """
935
978
  Query segments
@@ -938,7 +981,7 @@ class StreamChatInterface(abc.ABC):
938
981
 
939
982
  @abc.abstractmethod
940
983
  def update_segment(
941
- self, segment_id: str, data: Dict
984
+ self, segment_id: str, data: SegmentData
942
985
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
943
986
  """
944
987
  Update a segment by id
@@ -954,9 +997,70 @@ class StreamChatInterface(abc.ABC):
954
997
  """
955
998
  pass
956
999
 
1000
+ @abc.abstractmethod
1001
+ def segment_target_exists(
1002
+ self, segment_id: str, target_id: str
1003
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1004
+ """
1005
+ Check if a target exists in a segment
1006
+ """
1007
+ pass
1008
+
1009
+ @abc.abstractmethod
1010
+ def add_segment_targets(
1011
+ self, segment_id: str, target_ids: List[str]
1012
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1013
+ """
1014
+ Add targets to a segment
1015
+ """
1016
+ pass
1017
+
1018
+ @abc.abstractmethod
1019
+ def query_segment_targets(
1020
+ self,
1021
+ segment_id: str,
1022
+ filter_conditions: Optional[Dict] = None,
1023
+ sort: Optional[List[SortParam]] = None,
1024
+ options: Optional[QuerySegmentTargetsOptions] = None,
1025
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1026
+ """
1027
+ Query targets in a segment
1028
+ """
1029
+ pass
1030
+
1031
+ @abc.abstractmethod
1032
+ def remove_segment_targets(
1033
+ self, segment_id: str, target_ids: List[str]
1034
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1035
+ """
1036
+ Delete targets from a segment
1037
+ """
1038
+ pass
1039
+
1040
+ @abc.abstractmethod
1041
+ def campaign(
1042
+ self, campaign_id: Optional[str], data: Optional[CampaignData]
1043
+ ) -> TCampaign: # type: ignore[type-var]
1044
+ """
1045
+ Creates a campaign object
1046
+ :param campaign_id: the campaign id
1047
+ :param data: campaign_id data
1048
+ :return: Campaign
1049
+ """
1050
+ pass
1051
+
957
1052
  @abc.abstractmethod
958
1053
  def create_campaign(
959
- self, campaign: Dict
1054
+ self, campaign_id: Optional[str], data: Optional[CampaignData]
1055
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1056
+ """
1057
+ Create a campaign
1058
+ """
1059
+ pass
1060
+
1061
+ @abc.abstractmethod
1062
+ def get_campaign(
1063
+ self, campaign_id: str
960
1064
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
961
1065
  """
962
1066
  Create a campaign
@@ -965,7 +1069,10 @@ class StreamChatInterface(abc.ABC):
965
1069
 
966
1070
  @abc.abstractmethod
967
1071
  def query_campaigns(
968
- self, **params: Any
1072
+ self,
1073
+ filter_conditions: Optional[Dict] = None,
1074
+ sort: Optional[List[SortParam]] = None,
1075
+ options: Optional[QueryCampaignsOptions] = None,
969
1076
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
970
1077
  """
971
1078
  Query campaigns
@@ -974,7 +1081,7 @@ class StreamChatInterface(abc.ABC):
974
1081
 
975
1082
  @abc.abstractmethod
976
1083
  def update_campaign(
977
- self, campaign_id: str, data: Dict
1084
+ self, campaign_id: str, data: CampaignData
978
1085
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
979
1086
  """
980
1087
  Update a campaign
@@ -991,11 +1098,14 @@ class StreamChatInterface(abc.ABC):
991
1098
  pass
992
1099
 
993
1100
  @abc.abstractmethod
994
- def schedule_campaign(
995
- self, campaign_id: str, scheduled_for: int = None
1101
+ def start_campaign(
1102
+ self,
1103
+ campaign_id: str,
1104
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
1105
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
996
1106
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
997
1107
  """
998
- Schedule a campaign at given time
1108
+ Start a campaign at given time or now if not specified
999
1109
  """
1000
1110
  pass
1001
1111
 
@@ -1004,16 +1114,7 @@ class StreamChatInterface(abc.ABC):
1004
1114
  self, campaign_id: str
1005
1115
  ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1006
1116
  """
1007
- Stop a in progress campaign
1008
- """
1009
- pass
1010
-
1011
- @abc.abstractmethod
1012
- def resume_campaign(
1013
- self, campaign_id: str
1014
- ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1015
- """
1016
- Resume a stopped campaign
1117
+ Stop an in progress campaign
1017
1118
  """
1018
1119
  pass
1019
1120
 
@@ -1026,15 +1127,6 @@ class StreamChatInterface(abc.ABC):
1026
1127
  """
1027
1128
  pass
1028
1129
 
1029
- @abc.abstractmethod
1030
- def query_recipients(
1031
- self, **params: Any
1032
- ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
1033
- """
1034
- Query recipients
1035
- """
1036
- pass
1037
-
1038
1130
  @abc.abstractmethod
1039
1131
  def revoke_tokens(
1040
1132
  self, since: Union[str, datetime.datetime]
@@ -0,0 +1,72 @@
1
+ import abc
2
+ from typing import Awaitable, Dict, List, Optional, Union
3
+
4
+ from stream_chat.base.client import StreamChatInterface
5
+ from stream_chat.types.base import SortParam
6
+ from stream_chat.types.segment import (
7
+ QuerySegmentTargetsOptions,
8
+ SegmentData,
9
+ SegmentType,
10
+ )
11
+ from stream_chat.types.stream_response import StreamResponse
12
+
13
+
14
+ class SegmentInterface(abc.ABC):
15
+ def __init__(
16
+ self,
17
+ client: StreamChatInterface,
18
+ segment_type: SegmentType,
19
+ segment_id: Optional[str] = None,
20
+ data: Optional[SegmentData] = None,
21
+ ):
22
+ self.segment_type = segment_type
23
+ self.segment_id = segment_id
24
+ self.client = client
25
+ self.data = data
26
+
27
+ @abc.abstractmethod
28
+ def create(
29
+ self, segment_id: Optional[str] = None, data: Optional[SegmentData] = None
30
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
31
+ pass
32
+
33
+ @abc.abstractmethod
34
+ def get(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
35
+ pass
36
+
37
+ @abc.abstractmethod
38
+ def update(
39
+ self, data: SegmentData
40
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
41
+ pass
42
+
43
+ @abc.abstractmethod
44
+ def delete(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
45
+ pass
46
+
47
+ @abc.abstractmethod
48
+ def target_exists(
49
+ self, target_id: str
50
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
51
+ pass
52
+
53
+ @abc.abstractmethod
54
+ def add_targets(
55
+ self, target_ids: List[str]
56
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
57
+ pass
58
+
59
+ @abc.abstractmethod
60
+ def query_targets(
61
+ self,
62
+ filter_conditions: Optional[Dict] = None,
63
+ sort: Optional[List[SortParam]] = None,
64
+ options: Optional[QuerySegmentTargetsOptions] = None,
65
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
66
+ pass
67
+
68
+ @abc.abstractmethod
69
+ def remove_targets(
70
+ self, target_ids: List[str]
71
+ ) -> Union[StreamResponse, Awaitable[StreamResponse]]:
72
+ pass
@@ -0,0 +1,60 @@
1
+ import datetime
2
+ from typing import Any, Optional, Union
3
+
4
+ from stream_chat.base.campaign import CampaignInterface
5
+ from stream_chat.types.campaign import CampaignData
6
+ from stream_chat.types.stream_response import StreamResponse
7
+
8
+
9
+ class Campaign(CampaignInterface):
10
+ def create(
11
+ self, campaign_id: Optional[str] = None, data: Optional[CampaignData] = None
12
+ ) -> StreamResponse:
13
+ if campaign_id is not None:
14
+ self.campaign_id = campaign_id
15
+ if data is not None:
16
+ self.data = self._merge_campaign_data(self.data, data)
17
+ state = self.client.create_campaign(
18
+ campaign_id=self.campaign_id, data=self.data
19
+ )
20
+
21
+ if self.campaign_id is None and state.is_ok() and "campaign" in state: # type: ignore
22
+ self.campaign_id = state["campaign"]["id"] # type: ignore
23
+ return state # type: ignore
24
+
25
+ def get(self) -> StreamResponse:
26
+ return self.client.get_campaign(campaign_id=self.campaign_id) # type: ignore
27
+
28
+ def update(self, data: CampaignData) -> StreamResponse:
29
+ return self.client.update_campaign( # type: ignore
30
+ campaign_id=self.campaign_id, data=data
31
+ )
32
+
33
+ def delete(self, **options: Any) -> StreamResponse:
34
+ return self.client.delete_campaign( # type: ignore
35
+ campaign_id=self.campaign_id, **options
36
+ )
37
+
38
+ def start(
39
+ self,
40
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
41
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
42
+ ) -> StreamResponse:
43
+ return self.client.start_campaign( # type: ignore
44
+ campaign_id=self.campaign_id, scheduled_for=scheduled_for, stop_at=stop_at
45
+ )
46
+
47
+ def stop(self) -> StreamResponse:
48
+ return self.client.stop_campaign(campaign_id=self.campaign_id) # type: ignore
49
+
50
+ @staticmethod
51
+ def _merge_campaign_data(
52
+ data1: Optional[CampaignData],
53
+ data2: Optional[CampaignData],
54
+ ) -> CampaignData:
55
+ if data1 is None:
56
+ return data2
57
+ if data2 is None:
58
+ return data1
59
+ data1.update(data2) # type: ignore
60
+ return data1
stream_chat/client.py CHANGED
@@ -2,16 +2,26 @@ import datetime
2
2
  import json
3
3
  import sys
4
4
  import warnings
5
- from typing import Any, Callable, Dict, Iterable, List, Union
5
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
6
6
  from urllib.parse import urlparse
7
7
  from urllib.request import Request, urlopen
8
8
 
9
+ from stream_chat.campaign import Campaign
10
+ from stream_chat.segment import Segment
11
+ from stream_chat.types.base import SortParam
12
+ from stream_chat.types.campaign import CampaignData, QueryCampaignsOptions
13
+ from stream_chat.types.segment import (
14
+ QuerySegmentsOptions,
15
+ QuerySegmentTargetsOptions,
16
+ SegmentData,
17
+ SegmentType,
18
+ )
19
+
9
20
  if sys.version_info >= (3, 8):
10
21
  from typing import Literal
11
22
  else:
12
23
  from typing_extensions import Literal
13
24
 
14
-
15
25
  import requests
16
26
 
17
27
  from stream_chat.__pkg__ import __version__
@@ -457,7 +467,7 @@ class StreamChat(StreamChatInterface):
457
467
  return self._parse_response(response)
458
468
 
459
469
  def create_blocklist(
460
- self, name: str, words: Iterable[str], type: str = "regular"
470
+ self, name: str, words: Iterable[str], type: str = "word"
461
471
  ) -> StreamResponse:
462
472
  return self.post(
463
473
  "blocklists", data={"name": name, "words": words, "type": type}
@@ -518,49 +528,146 @@ class StreamChat(StreamChatInterface):
518
528
  def list_roles(self) -> StreamResponse:
519
529
  return self.get("roles")
520
530
 
521
- def create_segment(self, segment: Dict) -> StreamResponse:
522
- return self.post("segments", data={"segment": segment})
531
+ def segment( # type: ignore
532
+ self,
533
+ segment_type: SegmentType,
534
+ segment_id: Optional[str] = None,
535
+ data: Optional[SegmentData] = None,
536
+ ) -> Segment:
537
+ return Segment(
538
+ client=self, segment_type=segment_type, segment_id=segment_id, data=data
539
+ )
540
+
541
+ def create_segment(
542
+ self,
543
+ segment_type: SegmentType,
544
+ segment_id: Optional[str] = None,
545
+ data: Optional[SegmentData] = None,
546
+ ) -> StreamResponse:
547
+ payload = {"type": segment_type.value}
548
+ if segment_id is not None:
549
+ payload["id"] = segment_id
550
+ if data is not None:
551
+ payload.update(cast(dict, data))
552
+ return self.post("segments", data=payload)
523
553
 
524
- def query_segments(self, **params: Any) -> StreamResponse:
525
- return self.get("segments", params={"payload": json.dumps(params)})
554
+ def get_segment(self, segment_id: str) -> StreamResponse:
555
+ return self.get(f"segments/{segment_id}")
526
556
 
527
- def update_segment(self, segment_id: str, data: Dict) -> StreamResponse:
528
- return self.put(f"segments/{segment_id}", data={"segment": data})
557
+ def query_segments(
558
+ self,
559
+ filter_conditions: Optional[Dict] = None,
560
+ sort: Optional[List[SortParam]] = None,
561
+ options: Optional[QuerySegmentsOptions] = None,
562
+ ) -> StreamResponse:
563
+ payload = {}
564
+ if filter_conditions is not None:
565
+ payload["filter"] = filter_conditions
566
+ if sort is not None:
567
+ payload["sort"] = sort # type: ignore
568
+ if options is not None:
569
+ payload.update(cast(dict, options))
570
+ return self.post("segments/query", data=payload)
571
+
572
+ def update_segment(self, segment_id: str, data: SegmentData) -> StreamResponse:
573
+ return self.put(f"segments/{segment_id}", data=data)
529
574
 
530
575
  def delete_segment(self, segment_id: str) -> StreamResponse:
531
576
  return self.delete(f"segments/{segment_id}")
532
577
 
533
- def create_campaign(self, campaign: Dict) -> StreamResponse:
534
- return self.post("campaigns", data={"campaign": campaign})
578
+ def segment_target_exists(self, segment_id: str, target_id: str) -> StreamResponse:
579
+ return self.get(f"segments/{segment_id}/target/{target_id}")
580
+
581
+ def add_segment_targets(
582
+ self, segment_id: str, target_ids: List[str]
583
+ ) -> StreamResponse:
584
+ return self.post(
585
+ f"segments/{segment_id}/addtargets", data={"target_ids": target_ids}
586
+ )
587
+
588
+ def query_segment_targets(
589
+ self,
590
+ segment_id: str,
591
+ filter_conditions: Optional[Dict[str, Any]] = None,
592
+ sort: Optional[List[SortParam]] = None,
593
+ options: Optional[QuerySegmentTargetsOptions] = None,
594
+ ) -> StreamResponse:
595
+ payload: Dict[str, Union[Dict[str, Any], List[SortParam]]] = {}
596
+ if filter_conditions is not None:
597
+ payload["filter"] = filter_conditions
598
+ if sort is not None:
599
+ payload["sort"] = sort
600
+ if options is not None:
601
+ payload.update(cast(dict, options))
602
+ return self.post(f"segments/{segment_id}/targets/query", data=payload)
603
+
604
+ def remove_segment_targets(
605
+ self, segment_id: str, target_ids: List[str]
606
+ ) -> StreamResponse:
607
+ return self.post(
608
+ f"segments/{segment_id}/deletetargets", data={"target_ids": target_ids}
609
+ )
610
+
611
+ def campaign( # type: ignore
612
+ self, campaign_id: Optional[str] = None, data: Optional[CampaignData] = None
613
+ ) -> Campaign:
614
+ return Campaign(client=self, campaign_id=campaign_id, data=data)
615
+
616
+ def create_campaign(
617
+ self, campaign_id: Optional[str] = None, data: CampaignData = None
618
+ ) -> StreamResponse:
619
+ payload = {"id": campaign_id}
620
+ if data is not None:
621
+ payload.update(cast(dict, data))
622
+ return self.post("campaigns", data=payload)
535
623
 
536
- def query_campaigns(self, **params: Any) -> StreamResponse:
537
- return self.get("campaigns", params={"payload": json.dumps(params)})
624
+ def get_campaign(self, campaign_id: str) -> StreamResponse:
625
+ return self.get(f"campaigns/{campaign_id}")
538
626
 
539
- def update_campaign(self, campaign_id: str, data: Dict) -> StreamResponse:
540
- return self.put(f"campaigns/{campaign_id}", data={"campaign": data})
627
+ def query_campaigns(
628
+ self,
629
+ filter_conditions: Optional[Dict[str, Any]] = None,
630
+ sort: Optional[List[SortParam]] = None,
631
+ options: Optional[QueryCampaignsOptions] = None,
632
+ ) -> StreamResponse:
633
+ payload = {}
634
+ if filter_conditions is not None:
635
+ payload["filter"] = filter_conditions
636
+ if sort is not None:
637
+ payload["sort"] = sort # type: ignore
638
+ if options is not None:
639
+ payload.update(cast(dict, options))
640
+ return self.post("campaigns/query", data=payload)
641
+
642
+ def update_campaign(self, campaign_id: str, data: CampaignData) -> StreamResponse:
643
+ return self.put(f"campaigns/{campaign_id}", data=data)
541
644
 
542
645
  def delete_campaign(self, campaign_id: str, **options: Any) -> StreamResponse:
543
- return self.delete(f"campaigns/{campaign_id}", params=options)
646
+ return self.delete(f"campaigns/{campaign_id}", options)
544
647
 
545
- def schedule_campaign(
546
- self, campaign_id: str, scheduled_for: int = None
648
+ def start_campaign(
649
+ self,
650
+ campaign_id: str,
651
+ scheduled_for: Optional[Union[str, datetime.datetime]] = None,
652
+ stop_at: Optional[Union[str, datetime.datetime]] = None,
547
653
  ) -> StreamResponse:
548
- return self.patch(
549
- f"campaigns/{campaign_id}/schedule", data={"scheduled_for": scheduled_for}
550
- )
654
+ payload = {}
655
+ if scheduled_for is not None:
656
+ if isinstance(scheduled_for, datetime.datetime):
657
+ scheduled_for = scheduled_for.isoformat()
658
+ payload["scheduled_for"] = scheduled_for
659
+ if stop_at is not None:
660
+ if isinstance(stop_at, datetime.datetime):
661
+ stop_at = stop_at.isoformat()
662
+ payload["stop_at"] = stop_at
663
+ return self.post(f"campaigns/{campaign_id}/start", data=payload)
551
664
 
552
665
  def stop_campaign(self, campaign_id: str) -> StreamResponse:
553
- return self.patch(f"campaigns/{campaign_id}/stop")
554
-
555
- def resume_campaign(self, campaign_id: str) -> StreamResponse:
556
- return self.patch(f"campaigns/{campaign_id}/resume")
666
+ return self.post(f"campaigns/{campaign_id}/stop")
557
667
 
558
668
  def test_campaign(self, campaign_id: str, users: Iterable[str]) -> StreamResponse:
559
669
  return self.post(f"campaigns/{campaign_id}/test", data={"users": users})
560
670
 
561
- def query_recipients(self, **params: Any) -> StreamResponse:
562
- return self.get("recipients", params={"payload": json.dumps(params)})
563
-
564
671
  def revoke_tokens(self, since: Union[str, datetime.datetime]) -> StreamResponse:
565
672
  if isinstance(since, datetime.datetime):
566
673
  since = since.isoformat()
stream_chat/segment.py ADDED
@@ -0,0 +1,63 @@
1
+ from typing import Dict, List, Optional
2
+
3
+ from stream_chat.base.segment import SegmentInterface
4
+ from stream_chat.types.base import SortParam
5
+ from stream_chat.types.segment import QuerySegmentTargetsOptions, SegmentData
6
+ from stream_chat.types.stream_response import StreamResponse
7
+
8
+
9
+ class Segment(SegmentInterface):
10
+ def create(
11
+ self, segment_id: Optional[str] = None, data: Optional[SegmentData] = None
12
+ ) -> StreamResponse:
13
+ if segment_id is not None:
14
+ self.segment_id = segment_id
15
+ if data is not None:
16
+ self.data = data
17
+
18
+ state = self.client.create_segment(
19
+ segment_type=self.segment_type, segment_id=self.segment_id, data=self.data
20
+ )
21
+
22
+ if self.segment_id is None and state.is_ok() and "segment" in state: # type: ignore
23
+ self.segment_id = state["segment"]["id"] # type: ignore
24
+ return state # type: ignore
25
+
26
+ def get(self) -> StreamResponse:
27
+ return self.client.get_segment(segment_id=self.segment_id) # type: ignore
28
+
29
+ def update(self, data: SegmentData) -> StreamResponse:
30
+ return self.client.update_segment( # type: ignore
31
+ segment_id=self.segment_id, data=data
32
+ )
33
+
34
+ def delete(self) -> StreamResponse:
35
+ return self.client.delete_segment(segment_id=self.segment_id) # type: ignore
36
+
37
+ def target_exists(self, target_id: str) -> StreamResponse:
38
+ return self.client.segment_target_exists( # type: ignore
39
+ segment_id=self.segment_id, target_id=target_id
40
+ )
41
+
42
+ def add_targets(self, target_ids: list) -> StreamResponse:
43
+ return self.client.add_segment_targets( # type: ignore
44
+ segment_id=self.segment_id, target_ids=target_ids
45
+ )
46
+
47
+ def query_targets(
48
+ self,
49
+ filter_conditions: Optional[Dict] = None,
50
+ sort: Optional[List[SortParam]] = None,
51
+ options: Optional[QuerySegmentTargetsOptions] = None,
52
+ ) -> StreamResponse:
53
+ return self.client.query_segment_targets( # type: ignore
54
+ segment_id=self.segment_id,
55
+ sort=sort,
56
+ filter_conditions=filter_conditions,
57
+ options=options,
58
+ )
59
+
60
+ def remove_targets(self, target_ids: list) -> StreamResponse:
61
+ return self.client.remove_segment_targets( # type: ignore
62
+ segment_id=self.segment_id, target_ids=target_ids
63
+ )
@@ -0,0 +1,45 @@
1
+ import sys
2
+ from enum import IntEnum
3
+ from typing import Optional
4
+
5
+ if sys.version_info >= (3, 8):
6
+ from typing import TypedDict
7
+ else:
8
+ from typing_extensions import TypedDict
9
+
10
+
11
+ class SortOrder(IntEnum):
12
+ """
13
+ Represents the sort order for a query.
14
+ """
15
+
16
+ ASC = 1
17
+ DESC = -1
18
+
19
+
20
+ class SortParam(TypedDict, total=False):
21
+ """
22
+ Represents a sort parameter for a query.
23
+
24
+ Parameters:
25
+ field: The field to sort by.
26
+ direction: The direction to sort by.
27
+ """
28
+
29
+ field: str
30
+ direction: SortOrder
31
+
32
+
33
+ class Pager(TypedDict, total=False):
34
+ """
35
+ Represents the data structure for a pager.
36
+
37
+ Parameters:
38
+ limit: The maximum number of items to return.
39
+ next: The next page token.
40
+ prev: The previous page token.
41
+ """
42
+
43
+ limit: Optional[int]
44
+ next: Optional[str]
45
+ prev: Optional[str]
@@ -0,0 +1,78 @@
1
+ import sys
2
+ from typing import Dict, List, Optional
3
+
4
+ if sys.version_info >= (3, 8):
5
+ from typing import TypedDict
6
+ else:
7
+ from typing_extensions import TypedDict
8
+
9
+ from stream_chat.types.base import Pager
10
+
11
+
12
+ class MessageTemplate(TypedDict, total=False):
13
+ """
14
+ Represents the data structure for a message template.
15
+
16
+ Parameters:
17
+ text: The text of the message.
18
+ attachments: List of the message attachments.
19
+ custom: Custom data.
20
+ """
21
+
22
+ text: str
23
+ attachments: Optional[List[Dict]]
24
+ custom: Optional[Dict]
25
+
26
+
27
+ class ChannelTemplate(TypedDict, total=False):
28
+ """
29
+ Represents the data structure for a channel template.
30
+
31
+ Parameters:
32
+ type: The type of channel.
33
+ id: The ID of the channel.
34
+ members: List of member IDs.
35
+ custom: Custom data.
36
+ """
37
+
38
+ type: str
39
+ id: Optional[str]
40
+ members: Optional[List[str]]
41
+ custom: Optional[Dict]
42
+
43
+
44
+ class CampaignData(TypedDict, total=False):
45
+ """
46
+ Represents the data structure for a campaign.
47
+
48
+ Either `segment_ids` or `user_ids` must be provided, but not both.
49
+
50
+ If `create_channels` is True, `channel_template` must be provided.
51
+
52
+ Parameters:
53
+ message_template: The template for the message to be sent in the campaign.
54
+ sender_id: The ID of the user who is sending the campaign.
55
+ segment_ids: List of segment IDs the campaign is targeting.
56
+ user_ids: List of individual user IDs the campaign is targeting.
57
+ create_channels: Flag to indicate if new channels should be created for the campaign.
58
+ channel_template: The template for channels to be created, if applicable.
59
+ name: The name of the campaign.
60
+ description: A description of the campaign.
61
+ skip_push: Flag to indicate if push notifications should be skipped.
62
+ skip_webhook: Flag to indicate if webhooks should be skipped.
63
+ """
64
+
65
+ message_template: MessageTemplate
66
+ sender_id: str
67
+ segment_ids: Optional[List[str]]
68
+ user_ids: Optional[List[str]]
69
+ create_channels: Optional[bool]
70
+ channel_template: Optional[ChannelTemplate]
71
+ name: Optional[str]
72
+ description: Optional[str]
73
+ skip_push: Optional[bool]
74
+ skip_webhook: Optional[bool]
75
+
76
+
77
+ class QueryCampaignsOptions(Pager, total=False):
78
+ pass
@@ -0,0 +1,46 @@
1
+ import sys
2
+ from enum import Enum
3
+ from typing import Dict, Optional
4
+
5
+ if sys.version_info >= (3, 8):
6
+ from typing import TypedDict
7
+ else:
8
+ from typing_extensions import TypedDict
9
+
10
+ from stream_chat.types.base import Pager
11
+
12
+
13
+ class SegmentType(Enum):
14
+ """
15
+ Represents the type of segment.
16
+
17
+ Attributes:
18
+ CHANNEL: A segment targeting channels.
19
+ USER: A segment targeting users.
20
+ """
21
+
22
+ CHANNEL = "channel"
23
+ USER = "user"
24
+
25
+
26
+ class SegmentData(TypedDict, total=False):
27
+ """
28
+ Represents the data structure for a segment.
29
+
30
+ Parameters:
31
+ name: The name of the segment.
32
+ description: A description of the segment.
33
+ filter: A filter to apply to the segment.
34
+ """
35
+
36
+ name: Optional[str]
37
+ description: Optional[str]
38
+ filter: Optional[Dict]
39
+
40
+
41
+ class QuerySegmentsOptions(Pager, total=False):
42
+ pass
43
+
44
+
45
+ class QuerySegmentTargetsOptions(Pager, total=False):
46
+ pass
@@ -65,3 +65,7 @@ class StreamResponse(dict):
65
65
  def status_code(self) -> int:
66
66
  """Returns the HTTP status code of the response."""
67
67
  return self.__status_code
68
+
69
+ def is_ok(self) -> bool:
70
+ """Returns True if the status code is in the 200 range."""
71
+ return 200 <= self.__status_code < 300
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stream-chat
3
- Version: 4.11.0
3
+ Version: 4.12.1
4
4
  Summary: Client for Stream Chat.
5
5
  Home-page: https://github.com/GetStream/stream-chat-python
6
6
  Author: Tommaso Barbugli
7
7
  Author-email: support@getstream.io
8
8
  Project-URL: Bug Tracker, https://github.com/GetStream/stream-chat-python/issues
9
9
  Project-URL: Documentation, https://getstream.io/activity-feeds/docs/python/?language=python
10
- Project-URL: Release Notes, https://github.com/GetStream/stream-chat-python/releases/tag/v4.11.0
10
+ Project-URL: Release Notes, https://github.com/GetStream/stream-chat-python/releases/tag/v4.12.1
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Intended Audience :: System Administrators
13
13
  Classifier: Operating System :: OS Independent
@@ -0,0 +1,29 @@
1
+ stream_chat/__init__.py,sha256=xYQuC8xcPLJxJnFWzaNaO-sVUc7IJZYe13OIPRaDBEA,116
2
+ stream_chat/__pkg__.py,sha256=n_cQUeLvytzgt4sytXXqvw9Fe8FMTfvFz-1c7JdKYjk,206
3
+ stream_chat/campaign.py,sha256=Z7bBo2rGMs02JkA1k9_206J0spcSLecjdhQuRnrc2Eo,2156
4
+ stream_chat/channel.py,sha256=SxlnRM8U1yo_k3wDBC4azz3_LDRPclDV5GBmGCfqB8U,7766
5
+ stream_chat/client.py,sha256=0PacvOGFy7FKIwYEex1VVkl604EWMeEJdmQ3NjpfW14,28160
6
+ stream_chat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ stream_chat/segment.py,sha256=NqfjNZ9i7RYlLkBohxu3-Xc4l3zo3fnKoh2IMLAUrmo,2387
8
+ stream_chat/async_chat/__init__.py,sha256=V6x7yDCdXbP3vMuSan6Xm7RE_njZUvflImqOxfKRt4Y,67
9
+ stream_chat/async_chat/campaign.py,sha256=Bc3iHZigxserUycZK4wDuXU0wXVH2G6fIDiYNVYPkeE,1885
10
+ stream_chat/async_chat/channel.py,sha256=yn9P7u1lJdwoCVw9gKotadq6s1rdYSM_wnxhdaZpnTI,8124
11
+ stream_chat/async_chat/client.py,sha256=KRebkAv-eMO87XYGbDWKlag3ba99KpEghjkbWmw5LrU,30314
12
+ stream_chat/async_chat/segment.py,sha256=qo8waiGTkRRDRFYKW0GtrcSgnBkDF-PVSaCSBEnYwAg,2473
13
+ stream_chat/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ stream_chat/base/campaign.py,sha256=dAVkTYyirEpgyOPn8JyDebNMKeJu02vTuqgmPvdQoWU,1449
15
+ stream_chat/base/channel.py,sha256=G6thUgND74z0gvI-N3b3vEsrDJ-D4MrAfW2um_E4x9w,13788
16
+ stream_chat/base/client.py,sha256=uY0OsIqOPHs1vWtdov6o4kAoo2IdEYqMb-KBG_kewqQ,40247
17
+ stream_chat/base/exceptions.py,sha256=eh1qW5d6zjUrlgsHNEBebAr0jVH2UupZ06w8sp2cseI,819
18
+ stream_chat/base/segment.py,sha256=_UONbdF4OjaGIpsTzyY5UN_U3VAfuZyAkJnS3RfsM5k,2020
19
+ stream_chat/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ stream_chat/types/base.py,sha256=JKlOxQVPvMClTo0ev63Ya-K-dX-98j95qSE-KKzziEE,858
21
+ stream_chat/types/campaign.py,sha256=JyAn08oMaEsxPSqmVTdsfJK4xpfErPQV7Xr7zLQm99M,2293
22
+ stream_chat/types/rate_limit.py,sha256=v3Z4Ur0yoEdFLiHa1CNABEej2nxPlHQ6Bpy2XxW-TYQ,165
23
+ stream_chat/types/segment.py,sha256=28pLB3ADJcF8Y5PZHa4VACVg-ySxM94nCiP4uNvQxfs,925
24
+ stream_chat/types/stream_response.py,sha256=jWKPrOU7u6dZ2SyOK53uQCXTstrL1HshO9fA7R6Bt_A,2391
25
+ stream_chat-4.12.1.dist-info/LICENSE,sha256=H66SBDuPWSRHzKPEdyjAk3C0THQRcGPfqqve4naQuu0,14424
26
+ stream_chat-4.12.1.dist-info/METADATA,sha256=URkKgtoi9hOx2F1cKXNLX27lb5xKPz559eGDPt4cKWY,7332
27
+ stream_chat-4.12.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
28
+ stream_chat-4.12.1.dist-info/top_level.txt,sha256=26uTfg4bWcEaFrVKlzGGgfONH1p5DDe21G07EKfYSvo,12
29
+ stream_chat-4.12.1.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- stream_chat/__init__.py,sha256=xYQuC8xcPLJxJnFWzaNaO-sVUc7IJZYe13OIPRaDBEA,116
2
- stream_chat/__pkg__.py,sha256=cKE0dhcuFNoI1Qxw7fK9cL1RP8-7ACqiQHVOJ5MyS4g,206
3
- stream_chat/channel.py,sha256=SxlnRM8U1yo_k3wDBC4azz3_LDRPclDV5GBmGCfqB8U,7766
4
- stream_chat/client.py,sha256=yIdkT1vTDKluM369jxo5F9xdbDVZBOUMOVZcFVE4Kv8,24333
5
- stream_chat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- stream_chat/async_chat/__init__.py,sha256=V6x7yDCdXbP3vMuSan6Xm7RE_njZUvflImqOxfKRt4Y,67
7
- stream_chat/async_chat/channel.py,sha256=yn9P7u1lJdwoCVw9gKotadq6s1rdYSM_wnxhdaZpnTI,8124
8
- stream_chat/async_chat/client.py,sha256=hzxydYb2Aj5rQqMF8Im-5GFQUYr0Lyyb2CyVQDCDyFU,26405
9
- stream_chat/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- stream_chat/base/channel.py,sha256=G6thUgND74z0gvI-N3b3vEsrDJ-D4MrAfW2um_E4x9w,13788
11
- stream_chat/base/client.py,sha256=FjfkHOh-BMpoejHAeNKa0q_5L1mGk6dE9aLpvxHyXac,37443
12
- stream_chat/base/exceptions.py,sha256=eh1qW5d6zjUrlgsHNEBebAr0jVH2UupZ06w8sp2cseI,819
13
- stream_chat/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- stream_chat/types/rate_limit.py,sha256=v3Z4Ur0yoEdFLiHa1CNABEej2nxPlHQ6Bpy2XxW-TYQ,165
15
- stream_chat/types/stream_response.py,sha256=UdMAORACY6UwYskDx_vNnMTmJral24ocqYvCR8TQQHs,2247
16
- stream_chat-4.11.0.dist-info/LICENSE,sha256=H66SBDuPWSRHzKPEdyjAk3C0THQRcGPfqqve4naQuu0,14424
17
- stream_chat-4.11.0.dist-info/METADATA,sha256=rZR3go2YcoU8ZUKVK6TnX960-JRv7QRhv8cv7nQbsK0,7332
18
- stream_chat-4.11.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
19
- stream_chat-4.11.0.dist-info/top_level.txt,sha256=26uTfg4bWcEaFrVKlzGGgfONH1p5DDe21G07EKfYSvo,12
20
- stream_chat-4.11.0.dist-info/RECORD,,