mosir-sdk-python 0.1.1__tar.gz → 0.1.3__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.
- mosir_sdk_python-0.1.3/PKG-INFO +386 -0
- mosir_sdk_python-0.1.3/README.md +360 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/public.graphqls +17 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/public.operations.graphql +24 -2
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/pyproject.toml +1 -1
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk/_operations.py +8 -2
- mosir_sdk_python-0.1.3/src/mosir_sdk_python.egg-info/PKG-INFO +386 -0
- mosir_sdk_python-0.1.1/PKG-INFO +0 -219
- mosir_sdk_python-0.1.1/README.md +0 -193
- mosir_sdk_python-0.1.1/src/mosir_sdk_python.egg-info/PKG-INFO +0 -219
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/LICENSE +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/MANIFEST.in +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/setup.cfg +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk/__init__.py +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk/client.py +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk/exceptions.py +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk/py.typed +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk_python.egg-info/SOURCES.txt +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk_python.egg-info/dependency_links.txt +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk_python.egg-info/requires.txt +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/src/mosir_sdk_python.egg-info/top_level.txt +0 -0
- {mosir_sdk_python-0.1.1 → mosir_sdk_python-0.1.3}/tests/test_client.py +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mosir-sdk-python
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Python SDK for the Mosir public GraphQL API
|
|
5
|
+
Author-email: catLee <leemiyinghao@gmx.com>
|
|
6
|
+
License-Expression: LGPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://beta.mosir.app
|
|
8
|
+
Project-URL: Repository, https://github.com/mosir-social/mosir-sdk-python
|
|
9
|
+
Project-URL: Issues, https://github.com/mosir-social/mosir-sdk-python/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/mosir-social/mosir-sdk-python#readme
|
|
11
|
+
Keywords: graphql,httpx,mosir,sdk,sse
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: graphql-core>=3.2.8
|
|
22
|
+
Requires-Dist: httpx>=0.28.1
|
|
23
|
+
Requires-Dist: httpx-sse>=0.4.3
|
|
24
|
+
Requires-Dist: pydantic>=2.13.4
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# mosir-sdk-python
|
|
28
|
+
|
|
29
|
+
Python SDK for the Mosir public GraphQL API.
|
|
30
|
+
|
|
31
|
+
## What this SDK provides
|
|
32
|
+
|
|
33
|
+
- generated operation registry from `public.operations.graphql`
|
|
34
|
+
- dynamic snake_case operation methods (query, mutation, subscription)
|
|
35
|
+
- optional Bearer token auth
|
|
36
|
+
- default endpoint: `https://beta.mosir.app/api/v1`
|
|
37
|
+
- SSE subscription support out of the box
|
|
38
|
+
- raw GraphQL access for developers who want direct control
|
|
39
|
+
|
|
40
|
+
## Transport choice
|
|
41
|
+
|
|
42
|
+
This SDK uses:
|
|
43
|
+
|
|
44
|
+
- `httpx` for queries and mutations
|
|
45
|
+
- `httpx-sse` for subscriptions
|
|
46
|
+
|
|
47
|
+
This keeps the package small while still supporting the preferred subscription transport.
|
|
48
|
+
WebSocket support is intentionally not bundled. If you want WebSocket subscriptions, use your own GraphQL/WebSocket client.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install mosir-sdk-python
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
or:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv add mosir-sdk-python
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
### Anonymous/public requests
|
|
65
|
+
|
|
66
|
+
Only public data needs no token.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import asyncio
|
|
70
|
+
|
|
71
|
+
from mosir_sdk import AsyncMosirClient
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def main() -> None:
|
|
75
|
+
async with AsyncMosirClient() as client:
|
|
76
|
+
post = await client.get_post(post_id="VLO8u7UXqclQ7byjfMEX0")
|
|
77
|
+
print(post["getPost"]["content"])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
asyncio.run(main())
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Authenticated requests
|
|
84
|
+
|
|
85
|
+
Use a token for authenticated operations such as notifications.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import asyncio
|
|
89
|
+
import os
|
|
90
|
+
|
|
91
|
+
from mosir_sdk import AsyncMosirClient
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def main() -> None:
|
|
95
|
+
async with AsyncMosirClient(token=os.getenv("MOSIR_API_TOKEN")) as client:
|
|
96
|
+
notifications = await client.get_notifications(limit=20)
|
|
97
|
+
print(notifications["getNotifications"]["edges"])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
asyncio.run(main())
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Custom endpoint
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from mosir_sdk import AsyncMosirClient
|
|
107
|
+
|
|
108
|
+
client = AsyncMosirClient(
|
|
109
|
+
token="YOUR_TOKEN",
|
|
110
|
+
endpoint="https://example.com/api/v1",
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Common usage examples
|
|
115
|
+
|
|
116
|
+
### Get a post
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import asyncio
|
|
120
|
+
|
|
121
|
+
from mosir_sdk import AsyncMosirClient
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def main() -> None:
|
|
125
|
+
async with AsyncMosirClient() as client:
|
|
126
|
+
post = await client.get_post(post_id="VLO8u7UXqclQ7byjfMEX0")
|
|
127
|
+
print(post["getPost"]["author"]["username"])
|
|
128
|
+
print(post["getPost"]["content"])
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
asyncio.run(main())
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Get replies under a post
|
|
135
|
+
|
|
136
|
+
Replies are exposed as nested GraphQL fields on `Post`, so this is a good case for direct GraphQL usage:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
import asyncio
|
|
140
|
+
|
|
141
|
+
from mosir_sdk import AsyncMosirClient
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def main() -> None:
|
|
145
|
+
async with AsyncMosirClient() as client:
|
|
146
|
+
replies = await client.request(
|
|
147
|
+
"""
|
|
148
|
+
query GetPostReplies($postId: ID!, $limit: Int) {
|
|
149
|
+
getPost(postId: $postId) {
|
|
150
|
+
id
|
|
151
|
+
commentsRecent(limit: $limit) {
|
|
152
|
+
edges {
|
|
153
|
+
id
|
|
154
|
+
content
|
|
155
|
+
createdAt
|
|
156
|
+
author {
|
|
157
|
+
id
|
|
158
|
+
username
|
|
159
|
+
displayName
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
pageInfo {
|
|
163
|
+
endCursor
|
|
164
|
+
hasNextPage
|
|
165
|
+
totalCount
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
""",
|
|
171
|
+
{
|
|
172
|
+
"postId": "VLO8u7UXqclQ7byjfMEX0",
|
|
173
|
+
"limit": 3,
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
print(replies["getPost"]["commentsRecent"]["edges"])
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
asyncio.run(main())
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Get notifications
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
import asyncio
|
|
187
|
+
import os
|
|
188
|
+
|
|
189
|
+
from mosir_sdk import AsyncMosirClient
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
async def main() -> None:
|
|
193
|
+
async with AsyncMosirClient(token=os.getenv("MOSIR_API_TOKEN")) as client:
|
|
194
|
+
notifications = await client.get_notifications(limit=20)
|
|
195
|
+
print(notifications["getNotifications"]["edges"])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
asyncio.run(main())
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Fetch media bytes from a `Media` result
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
import asyncio
|
|
205
|
+
|
|
206
|
+
from mosir_sdk import AsyncMosirClient
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def main() -> None:
|
|
210
|
+
async with AsyncMosirClient() as client:
|
|
211
|
+
post = await client.get_post(post_id="VLO8u7UXqclQ7byjfMEX0")
|
|
212
|
+
media = post["getPost"]["attachments"][0]["media"]
|
|
213
|
+
media_bytes = await client.fetch_media(media)
|
|
214
|
+
print(len(media_bytes))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
asyncio.run(main())
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Fetch preview image for a post, profile, or collection
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
import asyncio
|
|
224
|
+
|
|
225
|
+
from mosir_sdk import AsyncMosirClient
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
async def main() -> None:
|
|
229
|
+
async with AsyncMosirClient() as client:
|
|
230
|
+
preview_url = client.get_preview_image_url("post", "VLO8u7UXqclQ7byjfMEX0")
|
|
231
|
+
print(preview_url)
|
|
232
|
+
|
|
233
|
+
preview_bytes = await client.fetch_preview_image("post", "VLO8u7UXqclQ7byjfMEX0")
|
|
234
|
+
print(len(preview_bytes))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
asyncio.run(main())
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## SSE subscriptions
|
|
241
|
+
|
|
242
|
+
Subscriptions let your app receive updates from Mosir in near real time without polling.
|
|
243
|
+
This SDK uses **SSE** (Server-Sent Events) for subscriptions by default.
|
|
244
|
+
|
|
245
|
+
A good example is a Discord bot:
|
|
246
|
+
- subscribe to `post_created_by_author`
|
|
247
|
+
- when a creator publishes something new, format it
|
|
248
|
+
- send a message into a Discord channel
|
|
249
|
+
|
|
250
|
+
That way the bot reacts as soon as something changes, instead of repeatedly calling the API every few seconds.
|
|
251
|
+
SSE is especially useful for backend workers, bots, notification relays, and other long-running processes that want a simple one-way stream of events from the server.
|
|
252
|
+
For public subscriptions like `post_created_by_author`, a token is not required.
|
|
253
|
+
|
|
254
|
+
Note: each SSE connection lasts at most 1 hour. In practice, network conditions may cause it to end earlier.
|
|
255
|
+
If you build a bot, worker, or relay process, make sure you implement reconnect logic.
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
import asyncio
|
|
259
|
+
|
|
260
|
+
from mosir_sdk import AsyncMosirClient
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
async def main() -> None:
|
|
264
|
+
async with AsyncMosirClient() as client:
|
|
265
|
+
profile = await client.get_account_profile(username="leemiyinghao")
|
|
266
|
+
author_id = profile["getAccountProfile"]["id"]
|
|
267
|
+
|
|
268
|
+
async for event in client.post_created_by_author(
|
|
269
|
+
author_id=author_id,
|
|
270
|
+
post_type="POST",
|
|
271
|
+
):
|
|
272
|
+
print(event["postCreatedByAuthor"]["id"])
|
|
273
|
+
print(event["postCreatedByAuthor"]["content"])
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
asyncio.run(main())
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
You can also use the lower-level operation subscription API:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
stream = client.subscribe_operation(
|
|
283
|
+
"post_created_by_author",
|
|
284
|
+
author_id=author_id,
|
|
285
|
+
post_type="POST",
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Raw GraphQL access
|
|
290
|
+
|
|
291
|
+
Authentication is optional. Pass `token` for authenticated operations, or omit it when accessing only public data.
|
|
292
|
+
|
|
293
|
+
### Operation usage
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
data = await client.operation("get_notifications", limit=20)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Raw GraphQL string usage
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
replies = await client.request(
|
|
303
|
+
"""
|
|
304
|
+
query GetPostReplies($postId: ID!, $limit: Int) {
|
|
305
|
+
getPost(postId: $postId) {
|
|
306
|
+
id
|
|
307
|
+
commentsRecent(limit: $limit) {
|
|
308
|
+
edges {
|
|
309
|
+
id
|
|
310
|
+
content
|
|
311
|
+
createdAt
|
|
312
|
+
author {
|
|
313
|
+
username
|
|
314
|
+
displayName
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
""",
|
|
321
|
+
{
|
|
322
|
+
"postId": "VLO8u7UXqclQ7byjfMEX0",
|
|
323
|
+
"limit": 3,
|
|
324
|
+
},
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## WebSocket usage
|
|
329
|
+
|
|
330
|
+
WebSocket transport is not bundled.
|
|
331
|
+
If you want it, use your own GraphQL WebSocket client against the same endpoint.
|
|
332
|
+
|
|
333
|
+
## Notes
|
|
334
|
+
|
|
335
|
+
- default endpoint: `https://beta.mosir.app/api/v1`
|
|
336
|
+
- `token` is optional for public data and required only for authenticated operations
|
|
337
|
+
- the same applies to subscriptions: public subscription data does not require a token
|
|
338
|
+
- snake_case methods are resolved dynamically from the operation registry
|
|
339
|
+
- media helpers are available through `select_media_file(...)` and `fetch_media(...)`
|
|
340
|
+
- preview image helpers are available through `get_preview_image_url(...)` and `fetch_preview_image(...)`
|
|
341
|
+
- subscriptions use SSE in this SDK
|
|
342
|
+
- direct GraphQL usage is supported through `operation(...)`, `request(...)`, `subscribe_operation(...)`, and `subscribe(...)`
|
|
343
|
+
|
|
344
|
+
## Development
|
|
345
|
+
|
|
346
|
+
### Install
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
task install
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Generate code
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
task codegen
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Typecheck
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
task pyright
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Build
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
task build
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Full check
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
task check
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Repo artifacts
|
|
377
|
+
|
|
378
|
+
- `public.graphqls` — copied public schema artifact
|
|
379
|
+
- `public.operations.graphql` — copied curated operation document
|
|
380
|
+
- `src/mosir_sdk/_operations.py` — generated operation registry
|
|
381
|
+
- `src/mosir_sdk/client.py` — async client and helpers
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
This project is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0).
|
|
386
|
+
See [`LICENSE`](./LICENSE) for details.
|