Skip to content

Commit 02ea360

Browse files
authored
feat: add full feature parity (#98)
1 parent f03b196 commit 02ea360

File tree

12 files changed

+262
-19
lines changed

12 files changed

+262
-19
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@ jobs:
1717
fail-fast: false
1818
max-parallel: 1
1919
matrix:
20-
python: [3.7, 3.8, 3.9, "3.10"]
20+
python: ["3.7", "3.8", "3.9", "3.10"]
2121
steps:
22-
- uses: actions/checkout@v2
22+
- name: Checkout
23+
uses: actions/checkout@v2
24+
with:
25+
fetch-depth: 0 # gives the commit message linter access to all previous commits
26+
27+
- name: Commit lint
28+
if: ${{ matrix.python == '3.7' }}
29+
uses: wagoid/commitlint-github-action@v4
30+
2331
- uses: actions/setup-python@v2
2432
with:
2533
python-version: ${{ matrix.python }}
@@ -28,7 +36,7 @@ jobs:
2836
run: pip install ".[test, ci]"
2937

3038
- name: Lint with ${{ matrix.python }}
31-
if: ${{ matrix.python == '3.8' }}
39+
if: ${{ matrix.python == '3.7' }}
3240
run: make lint
3341

3442
- name: Install, test and code coverage with ${{ matrix.python }}

README.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ Install deps
136136
pip install .[test, ci]
137137
```
138138

139-
First, make sure you can run the test suite. Tests are run via py.test
139+
First, make sure you can run the test suite. Tests are run via pytest.
140140

141141
```bash
142142
export STREAM_KEY=my_api_key
@@ -145,23 +145,17 @@ export STREAM_SECRET=my_api_secret
145145
make test
146146
```
147147

148+
> 💡 If you're on a Unix system, you could also use [direnv](https://direnv.net/) to set up these env vars.
149+
148150
Run linters
149151

150152
```bash
151153
make lint
152154
```
153155

156+
## We are hiring!
154157

155-
### Releasing a new version
156-
157-
In order to release new version you need to be a maintainer on Pypi.
158-
159-
- Update CHANGELOG
160-
- Make sure you have twine installed (pip install twine)
161-
- Update the version on setup.py
162-
- Commit and push to Github
163-
- Create a new tag for the version (eg. `v2.9.0`)
164-
- Create a new dist with python `python setup.py sdist bdist_wheel`
165-
- Upload the new distributable with twine `twine upload dist/*`
158+
We've recently closed a [$38 million Series B funding round](https://techcrunch.com/2021/03/04/stream-raises-38m-as-its-chat-and-activity-feed-apis-power-communications-for-1b-users/) and we keep actively growing.
159+
Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world.
166160

167-
If unsure you can also test using the Pypi test servers `twine upload --repository-url https://test.pypi.org/legacy/ dist/stream-chat-VERSION-NAME.tar.gz`
161+
Check out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs).

stream_chat/async_chat/channel.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ async def send_message(
1212
payload = {"message": add_user_id(message, user_id), **options}
1313
return await self.client.post(f"{self.url}/message", data=payload)
1414

15+
async def get_messages(self, message_ids: List[str]) -> StreamResponse:
16+
return await self.client.get(
17+
f"{self.url}/messages", params={"ids": ",".join(message_ids)}
18+
)
19+
1520
async def send_event(self, event: Dict, user_id: str) -> StreamResponse:
1621
payload = {"event": add_user_id(event, user_id)}
1722
return await self.client.post(f"{self.url}/event", data=payload)

stream_chat/async_chat/client.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ async def unban_user(self, target_id: str, **options: Any) -> StreamResponse:
164164
params = {"target_user_id": target_id, **options}
165165
return await self.delete("moderation/ban", params)
166166

167+
async def query_banned_users(self, query_conditions: Dict) -> StreamResponse:
168+
return await self.get(
169+
"query_banned_users", params={"payload": json.dumps(query_conditions)}
170+
)
171+
172+
async def run_message_action(self, message_id: str, data: Dict) -> StreamResponse:
173+
return await self.post(f"messages/{message_id}/action", data=data)
174+
167175
async def flag_message(self, target_id: str, **options: Any) -> StreamResponse:
168176
data = {"target_message_id": target_id, **options}
169177
return await self.post("moderation/flag", data=data)
@@ -228,6 +236,11 @@ async def unmute_user(self, target_id: str, user_id: str) -> StreamResponse:
228236
async def mark_all_read(self, user_id: str) -> StreamResponse:
229237
return await self.post("channels/read", data={"user": {"id": user_id}})
230238

239+
async def translate_message(self, message_id: str, language: str) -> StreamResponse:
240+
return await self.post(
241+
f"messages/{message_id}/translate", data={"language": language}
242+
)
243+
231244
async def pin_message(
232245
self, message_id: str, user_id: str, expiration: int = None
233246
) -> StreamResponse:
@@ -422,12 +435,18 @@ async def update_blocklist(self, name: str, words: Iterable[str]) -> StreamRespo
422435
async def delete_blocklist(self, name: str) -> StreamResponse:
423436
return await self.delete(f"blocklists/{name}")
424437

438+
async def check_push(self, push_data: Dict) -> StreamResponse:
439+
return await self.post("check_push", data=push_data)
440+
425441
async def check_sqs(
426442
self, sqs_key: str = None, sqs_secret: str = None, sqs_url: str = None
427443
) -> StreamResponse:
428444
data = {"sqs_key": sqs_key, "sqs_secret": sqs_secret, "sqs_url": sqs_url}
429445
return await self.post("check_sqs", data=data)
430446

447+
async def set_guest_user(self, guest_user: Dict) -> StreamResponse:
448+
return await self.post("guest", data=dict(user=guest_user))
449+
431450
async def get_permission(self, id: str) -> StreamResponse:
432451
return await self.get(f"permissions/{id}")
433452

stream_chat/base/channel.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ def create(self, user_id: str) -> Union[StreamResponse, Awaitable[StreamResponse
9595
"""
9696
pass
9797

98+
@abc.abstractmethod
99+
def get_messages(
100+
self, message_ids: List[str]
101+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
102+
"""
103+
Gets many messages
104+
105+
:param message_ids: list of message ids to returns
106+
:return:
107+
"""
108+
pass
109+
98110
@abc.abstractmethod
99111
def query(self, **options: Any) -> Union[StreamResponse, Awaitable[StreamResponse]]:
100112
"""

stream_chat/base/client.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import datetime
44
import hashlib
55
import hmac
6+
import os
67
from typing import Any, Awaitable, Dict, Iterable, List, TypeVar, Union
78

89
import jwt
@@ -19,8 +20,18 @@ def __init__(
1920
self.api_key = api_key
2021
self.api_secret = api_secret
2122
self.timeout = timeout
23+
24+
if os.getenv("STREAM_CHAT_TIMEOUT"):
25+
self.timeout = float(os.environ["STREAM_CHAT_TIMEOUT"])
26+
2227
self.options = options
23-
self.base_url = options.get("base_url", "https://chat.stream-io-api.com")
28+
self.base_url = "https://chat.stream-io-api.com"
29+
30+
if options.get("base_url"):
31+
self.base_url = options["base_url"]
32+
elif os.getenv("STREAM_CHAT_URL"):
33+
self.base_url = os.environ["STREAM_CHAT_URL"]
34+
2435
self.auth_token = jwt.encode(
2536
{"server": True}, self.api_secret, algorithm="HS256"
2637
)
@@ -93,6 +104,18 @@ def update_app_settings(
93104
def get_app_settings(self) -> Union[StreamResponse, Awaitable[StreamResponse]]:
94105
pass
95106

107+
@abc.abstractmethod
108+
def set_guest_user(
109+
self, guest_user: Dict
110+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
111+
"""
112+
Sets up a new guest user
113+
114+
:param guest_user: the guest user data
115+
:return:
116+
"""
117+
pass
118+
96119
@abc.abstractmethod
97120
def update_users(
98121
self, users: List[Dict]
@@ -179,6 +202,18 @@ def unban_user(
179202
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
180203
pass
181204

205+
@abc.abstractmethod
206+
def query_banned_users(
207+
self, query_conditions: Dict
208+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
209+
pass
210+
211+
@abc.abstractmethod
212+
def run_message_action(
213+
self, message_id: str, data: Dict
214+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
215+
pass
216+
182217
@abc.abstractmethod
183218
def flag_message(
184219
self, target_id: str, **options: Any
@@ -243,6 +278,19 @@ def mark_all_read(
243278
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
244279
pass
245280

281+
@abc.abstractmethod
282+
def translate_message(
283+
self, message_id: str, language: str
284+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
285+
"""
286+
Translates a message
287+
288+
:param message_id: Id of the message to be translated
289+
:param language: Target language of the translation
290+
:return:
291+
"""
292+
pass
293+
246294
@abc.abstractmethod
247295
def pin_message(
248296
self, message_id: str, user_id: str, expiration: int = None
@@ -506,6 +554,18 @@ def delete_blocklist(
506554
"""
507555
pass
508556

557+
@abc.abstractmethod
558+
def check_push(
559+
self, push_data: Dict
560+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
561+
"""
562+
Check push notification settings
563+
564+
:param push_data: Test data for testing push notification settings
565+
:return:
566+
"""
567+
pass
568+
509569
@abc.abstractmethod
510570
def check_sqs(
511571
self, sqs_key: str = None, sqs_secret: str = None, sqs_url: str = None

stream_chat/channel.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ def create(self, user_id: str) -> StreamResponse:
3434
self.custom_data["created_by"] = {"id": user_id}
3535
return self.query(watch=False, state=False, presence=False)
3636

37+
def get_messages(self, message_ids: List[str]) -> StreamResponse:
38+
return self.client.get(
39+
f"{self.url}/messages", params={"ids": ",".join(message_ids)}
40+
)
41+
3742
def query(self, **options: Any) -> StreamResponse:
3843
payload = {"state": True, "data": self.custom_data, **options}
3944

stream_chat/client.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
from stream_chat.__pkg__ import __version__
1010
from stream_chat.base.client import StreamChatInterface
1111
from stream_chat.base.exceptions import StreamAPIException
12+
from stream_chat.channel import Channel
1213
from stream_chat.types.stream_response import StreamResponse
1314

14-
from .channel import Channel
15-
1615

1716
def get_user_agent() -> str:
1817
return f"stream-python-client-{__version__}"
@@ -106,6 +105,9 @@ def update_app_settings(self, **settings: Any) -> StreamResponse:
106105
def get_app_settings(self) -> StreamResponse:
107106
return self.get("app")
108107

108+
def set_guest_user(self, guest_user: Dict) -> StreamResponse:
109+
return self.post("guest", data=dict(user=guest_user))
110+
109111
def update_users(self, users: List[Dict]) -> StreamResponse:
110112
return self.post("users", data={"users": {u["id"]: u for u in users}})
111113

@@ -151,6 +153,14 @@ def unban_user(self, target_id: str, **options: Any) -> StreamResponse:
151153
params = {"target_user_id": target_id, **options}
152154
return self.delete("moderation/ban", params)
153155

156+
def query_banned_users(self, query_conditions: Dict) -> StreamResponse:
157+
return self.get(
158+
"query_banned_users", params={"payload": json.dumps(query_conditions)}
159+
)
160+
161+
def run_message_action(self, message_id: str, data: Dict) -> StreamResponse:
162+
return self.post(f"messages/{message_id}/action", data=data)
163+
154164
def flag_message(self, target_id: str, **options: Any) -> StreamResponse:
155165
data = {"target_message_id": target_id, **options}
156166
return self.post("moderation/flag", data=data)
@@ -213,6 +223,11 @@ def unmute_user(self, target_id: str, user_id: str) -> StreamResponse:
213223
def mark_all_read(self, user_id: str) -> StreamResponse:
214224
return self.post("channels/read", data={"user": {"id": user_id}})
215225

226+
def translate_message(self, message_id: str, language: str) -> StreamResponse:
227+
return self.post(
228+
f"messages/{message_id}/translate", data={"language": language}
229+
)
230+
216231
def pin_message(
217232
self, message_id: str, user_id: str, expiration: int = None
218233
) -> StreamResponse:
@@ -400,6 +415,9 @@ def update_blocklist(self, name: str, words: Iterable[str]) -> StreamResponse:
400415
def delete_blocklist(self, name: str) -> StreamResponse:
401416
return self.delete(f"blocklists/{name}")
402417

418+
def check_push(self, push_data: Dict) -> StreamResponse:
419+
return self.post("check_push", data=push_data)
420+
403421
def check_sqs(
404422
self, sqs_key: str = None, sqs_secret: str = None, sqs_url: str = None
405423
) -> StreamResponse:

stream_chat/tests/async_chat/test_channel.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ async def test_assign_roles_moderators(self, channel: Channel, random_user: Dict
144144
assert len(response["members"]) == 1
145145
assert response["members"][0]["channel_role"] == "channel_member"
146146

147+
async def test_get_messages(self, channel: Channel, random_user: Dict):
148+
msg_id = str(uuid.uuid4())
149+
await channel.send_message(
150+
{"id": msg_id, "text": "helloworld"}, random_user["id"]
151+
)
152+
resp = await channel.get_messages([msg_id])
153+
assert len(resp["messages"]) == 1
154+
147155
async def test_mark_read(self, channel: Channel, random_user: Dict):
148156
response = await channel.mark_read(random_user["id"])
149157
assert "event" in response

0 commit comments

Comments
 (0)