Skip to content

Commit b73273f

Browse files
feat(api): add upload asset and edit message endpoints
1 parent df55111 commit b73273f

20 files changed

+638
-39
lines changed

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 15
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-bea2e5f3b01053a66261a824c75c2640856d0ba00ad795ab71734c4ab9ae33b0.yml
3-
openapi_spec_hash: d766f6e344c12ca6d23e6ef6713b38c4
4-
config_hash: c1e9e27b4965dd90c14c8e88c9036be4
1+
configured_endpoints: 17
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-0e4d333e81e670e605a6706dbb365bc96957a59331fdc87dd1550c59880cb130.yml
3+
openapi_spec_hash: 564146e6d318ecb486ff7c0d7983b398
4+
config_hash: e3418e22e2ca1df0f8a9fcaf7153285a

api.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@ Methods:
6969
Types:
7070

7171
```python
72-
from beeper_desktop_api.types import MessageSendResponse
72+
from beeper_desktop_api.types import MessageUpdateResponse, MessageSendResponse
7373
```
7474

7575
Methods:
7676

77+
- <code title="put /v1/chats/{chatID}/messages/{messageID}">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">update</a>(message_id, \*, chat_id, \*\*<a href="src/beeper_desktop_api/types/message_update_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_update_response.py">MessageUpdateResponse</a></code>
7778
- <code title="get /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">list</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_list_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorSortKey[Message]</a></code>
7879
- <code title="get /v1/messages/search">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">search</a>(\*\*<a href="src/beeper_desktop_api/types/message_search_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorSearch[Message]</a></code>
7980
- <code title="post /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">send</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_send_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_send_response.py">MessageSendResponse</a></code>
@@ -83,9 +84,10 @@ Methods:
8384
Types:
8485

8586
```python
86-
from beeper_desktop_api.types import AssetDownloadResponse
87+
from beeper_desktop_api.types import AssetDownloadResponse, AssetUploadResponse
8788
```
8889

8990
Methods:
9091

9192
- <code title="post /v1/assets/download">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">download</a>(\*\*<a href="src/beeper_desktop_api/types/asset_download_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/asset_download_response.py">AssetDownloadResponse</a></code>
93+
- <code title="post /v1/assets/upload">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">upload</a>(\*\*<a href="src/beeper_desktop_api/types/asset_upload_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/asset_upload_response.py">AssetUploadResponse</a></code>

src/beeper_desktop_api/_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def search(
300300
via search-chats. Uses the same sorting as the chat search in the app.
301301
302302
Args:
303-
query: User-typed search text. Literal word matching (NOT semantic).
303+
query: User-typed search text. Literal word matching (non-semantic).
304304
305305
extra_headers: Send extra headers
306306
@@ -588,7 +588,7 @@ async def search(
588588
via search-chats. Uses the same sorting as the chat search in the app.
589589
590590
Args:
591-
query: User-typed search text. Literal word matching (NOT semantic).
591+
query: User-typed search text. Literal word matching (non-semantic).
592592
593593
extra_headers: Send extra headers
594594

src/beeper_desktop_api/resources/accounts/contacts.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ def search(
5555
extra_body: Body | None = None,
5656
timeout: float | httpx.Timeout | None | NotGiven = not_given,
5757
) -> ContactSearchResponse:
58-
"""
59-
Search contacts across on a specific account using the network's search API.
60-
Only use for creating new chats.
58+
"""Search contacts on a specific account using the network's search API.
59+
60+
Only use
61+
for creating new chats.
6162
6263
Args:
6364
account_id: Account ID this resource belongs to.
@@ -121,9 +122,10 @@ async def search(
121122
extra_body: Body | None = None,
122123
timeout: float | httpx.Timeout | None | NotGiven = not_given,
123124
) -> ContactSearchResponse:
124-
"""
125-
Search contacts across on a specific account using the network's search API.
126-
Only use for creating new chats.
125+
"""Search contacts on a specific account using the network's search API.
126+
127+
Only use
128+
for creating new chats.
127129
128130
Args:
129131
account_id: Account ID this resource belongs to.

src/beeper_desktop_api/resources/assets.py

Lines changed: 132 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
from __future__ import annotations
44

5+
from typing import Mapping, cast
6+
57
import httpx
68

7-
from ..types import asset_download_params
8-
from .._types import Body, Query, Headers, NotGiven, not_given
9-
from .._utils import maybe_transform, async_maybe_transform
9+
from ..types import asset_upload_params, asset_download_params
10+
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
11+
from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
1012
from .._compat import cached_property
1113
from .._resource import SyncAPIResource, AsyncAPIResource
1214
from .._response import (
@@ -16,6 +18,7 @@
1618
async_to_streamed_response_wrapper,
1719
)
1820
from .._base_client import make_request_options
21+
from ..types.asset_upload_response import AssetUploadResponse
1922
from ..types.asset_download_response import AssetDownloadResponse
2023

2124
__all__ = ["AssetsResource", "AsyncAssetsResource"]
@@ -78,6 +81,63 @@ def download(
7881
cast_to=AssetDownloadResponse,
7982
)
8083

84+
def upload(
85+
self,
86+
*,
87+
content: str,
88+
file_name: str | Omit = omit,
89+
mime_type: str | Omit = omit,
90+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
91+
# The extra values given here take precedence over values defined on the client or passed to this method.
92+
extra_headers: Headers | None = None,
93+
extra_query: Query | None = None,
94+
extra_body: Body | None = None,
95+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
96+
) -> AssetUploadResponse:
97+
"""Upload a file to a temporary location.
98+
99+
Supports JSON body with base64 `content`
100+
field, or multipart/form-data with `file` field. Returns a local file URL that
101+
can be used when sending messages with attachments.
102+
103+
Args:
104+
content: Base64-encoded file content (max ~500MB decoded)
105+
106+
file_name: Original filename. Generated if omitted
107+
108+
mime_type: MIME type. Auto-detected from magic bytes if omitted
109+
110+
extra_headers: Send extra headers
111+
112+
extra_query: Add additional query parameters to the request
113+
114+
extra_body: Add additional JSON properties to the request
115+
116+
timeout: Override the client-level default timeout for this request, in seconds
117+
"""
118+
body = deepcopy_minimal(
119+
{
120+
"content": content,
121+
"file_name": file_name,
122+
"mime_type": mime_type,
123+
}
124+
)
125+
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
126+
if files:
127+
# It should be noted that the actual Content-Type header that will be
128+
# sent to the server will contain a `boundary` parameter, e.g.
129+
# multipart/form-data; boundary=---abc--
130+
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
131+
return self._post(
132+
"/v1/assets/upload",
133+
body=maybe_transform(body, asset_upload_params.AssetUploadParams),
134+
files=files,
135+
options=make_request_options(
136+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
137+
),
138+
cast_to=AssetUploadResponse,
139+
)
140+
81141

82142
class AsyncAssetsResource(AsyncAPIResource):
83143
"""Manage assets in Beeper Desktop, like message attachments"""
@@ -136,6 +196,63 @@ async def download(
136196
cast_to=AssetDownloadResponse,
137197
)
138198

199+
async def upload(
200+
self,
201+
*,
202+
content: str,
203+
file_name: str | Omit = omit,
204+
mime_type: str | Omit = omit,
205+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
206+
# The extra values given here take precedence over values defined on the client or passed to this method.
207+
extra_headers: Headers | None = None,
208+
extra_query: Query | None = None,
209+
extra_body: Body | None = None,
210+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
211+
) -> AssetUploadResponse:
212+
"""Upload a file to a temporary location.
213+
214+
Supports JSON body with base64 `content`
215+
field, or multipart/form-data with `file` field. Returns a local file URL that
216+
can be used when sending messages with attachments.
217+
218+
Args:
219+
content: Base64-encoded file content (max ~500MB decoded)
220+
221+
file_name: Original filename. Generated if omitted
222+
223+
mime_type: MIME type. Auto-detected from magic bytes if omitted
224+
225+
extra_headers: Send extra headers
226+
227+
extra_query: Add additional query parameters to the request
228+
229+
extra_body: Add additional JSON properties to the request
230+
231+
timeout: Override the client-level default timeout for this request, in seconds
232+
"""
233+
body = deepcopy_minimal(
234+
{
235+
"content": content,
236+
"file_name": file_name,
237+
"mime_type": mime_type,
238+
}
239+
)
240+
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
241+
if files:
242+
# It should be noted that the actual Content-Type header that will be
243+
# sent to the server will contain a `boundary` parameter, e.g.
244+
# multipart/form-data; boundary=---abc--
245+
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
246+
return await self._post(
247+
"/v1/assets/upload",
248+
body=await async_maybe_transform(body, asset_upload_params.AssetUploadParams),
249+
files=files,
250+
options=make_request_options(
251+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
252+
),
253+
cast_to=AssetUploadResponse,
254+
)
255+
139256

140257
class AssetsResourceWithRawResponse:
141258
def __init__(self, assets: AssetsResource) -> None:
@@ -144,6 +261,9 @@ def __init__(self, assets: AssetsResource) -> None:
144261
self.download = to_raw_response_wrapper(
145262
assets.download,
146263
)
264+
self.upload = to_raw_response_wrapper(
265+
assets.upload,
266+
)
147267

148268

149269
class AsyncAssetsResourceWithRawResponse:
@@ -153,6 +273,9 @@ def __init__(self, assets: AsyncAssetsResource) -> None:
153273
self.download = async_to_raw_response_wrapper(
154274
assets.download,
155275
)
276+
self.upload = async_to_raw_response_wrapper(
277+
assets.upload,
278+
)
156279

157280

158281
class AssetsResourceWithStreamingResponse:
@@ -162,6 +285,9 @@ def __init__(self, assets: AssetsResource) -> None:
162285
self.download = to_streamed_response_wrapper(
163286
assets.download,
164287
)
288+
self.upload = to_streamed_response_wrapper(
289+
assets.upload,
290+
)
165291

166292

167293
class AsyncAssetsResourceWithStreamingResponse:
@@ -171,3 +297,6 @@ def __init__(self, assets: AsyncAssetsResource) -> None:
171297
self.download = async_to_streamed_response_wrapper(
172298
assets.download,
173299
)
300+
self.upload = async_to_streamed_response_wrapper(
301+
assets.upload,
302+
)

0 commit comments

Comments
 (0)