truthsocial-py 0.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.
- truthsocial_py-0.1.0/LICENSE +21 -0
- truthsocial_py-0.1.0/PKG-INFO +285 -0
- truthsocial_py-0.1.0/README.md +258 -0
- truthsocial_py-0.1.0/pyproject.toml +46 -0
- truthsocial_py-0.1.0/setup.cfg +4 -0
- truthsocial_py-0.1.0/src/truthsocial_py/__init__.py +56 -0
- truthsocial_py-0.1.0/src/truthsocial_py/_version.py +1 -0
- truthsocial_py-0.1.0/src/truthsocial_py/app.py +273 -0
- truthsocial_py-0.1.0/src/truthsocial_py/client.py +842 -0
- truthsocial_py-0.1.0/src/truthsocial_py/errors.py +73 -0
- truthsocial_py-0.1.0/src/truthsocial_py/models.py +173 -0
- truthsocial_py-0.1.0/src/truthsocial_py/py.typed +0 -0
- truthsocial_py-0.1.0/src/truthsocial_py.egg-info/PKG-INFO +285 -0
- truthsocial_py-0.1.0/src/truthsocial_py.egg-info/SOURCES.txt +17 -0
- truthsocial_py-0.1.0/src/truthsocial_py.egg-info/dependency_links.txt +1 -0
- truthsocial_py-0.1.0/src/truthsocial_py.egg-info/requires.txt +1 -0
- truthsocial_py-0.1.0/src/truthsocial_py.egg-info/top_level.txt +1 -0
- truthsocial_py-0.1.0/tests/test_app.py +326 -0
- truthsocial_py-0.1.0/tests/test_client.py +736 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jack Sweeney
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: truthsocial-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An unofficial typed Python client for Truth Social
|
|
5
|
+
Author-email: Jack Sweeney <jackhsweeney1@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Jxck-S/truthsocial-py
|
|
8
|
+
Project-URL: Repository, https://github.com/Jxck-S/truthsocial-py
|
|
9
|
+
Project-URL: Issues, https://github.com/Jxck-S/truthsocial-py/issues
|
|
10
|
+
Keywords: truth-social,truthsocial,api,client,mastodon
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Internet
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: httpx<1,>=0.27
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# truthsocial-py
|
|
29
|
+
|
|
30
|
+
An unofficial, typed Python client for Truth Social.
|
|
31
|
+
|
|
32
|
+
`truthsocial-py` supports OAuth app discovery, manual app configuration, multiple
|
|
33
|
+
independent user sessions, text and media posts, replies, and structured API
|
|
34
|
+
errors.
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> Truth Social does not publish this interface as a stable public API.
|
|
38
|
+
> Endpoints and payloads may change.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- Discover the OAuth app identity from the deployed Truth Social web client
|
|
43
|
+
- Configure an OAuth app identity manually
|
|
44
|
+
- Log in multiple users with isolated tokens, HTTP state, and session IDs
|
|
45
|
+
- Rediscover rotated web-app credentials automatically or on demand
|
|
46
|
+
- Publish text and image posts
|
|
47
|
+
- Reply to a status by ID or `Status` object
|
|
48
|
+
- Reuse existing access tokens
|
|
49
|
+
- Handle authentication, rate-limit, transport, and protocol errors
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
Python 3.10 or newer is required.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip install truthsocial-py
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The distribution is `truthsocial-py`; the import package is `truthsocial_py`:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from truthsocial_py import TruthSocialApp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
To work on the library itself:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/Jxck-S/truthsocial-py.git
|
|
69
|
+
cd truthsocial-py
|
|
70
|
+
python3 -m venv .venv
|
|
71
|
+
source .venv/bin/activate
|
|
72
|
+
python -m pip install -e .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
Create an app from the current Truth Social web deployment, then log in:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import getpass
|
|
81
|
+
|
|
82
|
+
from truthsocial_py import TruthSocialApp
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
app = TruthSocialApp.from_web()
|
|
86
|
+
|
|
87
|
+
with app.login(
|
|
88
|
+
username=input("Truth Social username: "),
|
|
89
|
+
password=getpass.getpass("Truth Social password: "),
|
|
90
|
+
) as client:
|
|
91
|
+
account = client.verify_credentials()
|
|
92
|
+
status = client.post_status("Hello from truthsocial-py!")
|
|
93
|
+
print(f"Posted as @{account.acct}: {status.url}")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## App identities
|
|
97
|
+
|
|
98
|
+
`TruthSocialApp` represents one OAuth application identity. Load the current
|
|
99
|
+
identity from the website:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
app = TruthSocialApp.from_web()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or provide one manually:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
app = TruthSocialApp(
|
|
109
|
+
client_id="your-client-id",
|
|
110
|
+
client_secret="your-client-secret",
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Web-loaded apps enable automatic rediscovery by default. If login returns
|
|
115
|
+
OAuth's `invalid_client` or `unauthorized_client` error, the app refreshes its
|
|
116
|
+
identity and retries once. Refresh it explicitly at any time:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
credentials = app.rediscover()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Rediscovery applies to future clients and login attempts. Existing clients
|
|
123
|
+
keep their current token and app snapshot. A manually configured app can opt
|
|
124
|
+
in with `auto_rediscover=True`.
|
|
125
|
+
|
|
126
|
+
## Multiple users
|
|
127
|
+
|
|
128
|
+
Every login returns a separate `TruthSocialClient`:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
alice = app.login("alice", "alice-password")
|
|
132
|
+
bob = app.login("bob", "bob-password")
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
alice.post_status("Posted by Alice")
|
|
136
|
+
bob.post_status("Posted by Bob")
|
|
137
|
+
finally:
|
|
138
|
+
alice.close()
|
|
139
|
+
bob.close()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Create a client from a previously saved token without logging in again:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
alice = app.new_client(access_token=load_alice_token())
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## User agent
|
|
149
|
+
|
|
150
|
+
Requests are sent with `truthsocial-py/<version>` by default, exposed as
|
|
151
|
+
`truthsocial_py.DEFAULT_USER_AGENT`. Override it on an app or a client:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from truthsocial_py import DEFAULT_USER_AGENT, TruthSocialApp
|
|
155
|
+
|
|
156
|
+
app = TruthSocialApp.from_web(user_agent="my-bot/2.0 (+https://example.com)")
|
|
157
|
+
client = app.new_client() # inherits the app's user agent
|
|
158
|
+
print(client.user_agent)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`TruthSocialClient(..., user_agent=...)` works the same way. The value must be
|
|
162
|
+
a non-empty, header-safe string; anything else raises `ConfigurationError`.
|
|
163
|
+
|
|
164
|
+
## Media posts
|
|
165
|
+
|
|
166
|
+
Pass paths through `media_files`; each file is uploaded before the status:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
status = client.post_status(
|
|
170
|
+
"A photo post",
|
|
171
|
+
media_files=["photo.png"],
|
|
172
|
+
idempotency_key="your-stable-unique-key",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
for attachment in status.media_attachments:
|
|
176
|
+
print(attachment.id, attachment.url)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Upload separately when you need the attachment first:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
attachment = client.upload_media("photo.png")
|
|
183
|
+
status = client.post_status(
|
|
184
|
+
"Uploaded separately",
|
|
185
|
+
media_ids=[attachment.id],
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`upload_media()` also accepts open binary files with optional `filename` and
|
|
190
|
+
`content_type` arguments. Do not combine `media_files` and `media_ids` in the
|
|
191
|
+
same call. Media types and size limits are controlled by Truth Social.
|
|
192
|
+
|
|
193
|
+
Truth Social currently accepts `public` visibility for these posting calls.
|
|
194
|
+
|
|
195
|
+
## Replies
|
|
196
|
+
|
|
197
|
+
Reply using a parent status ID:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
reply = client.reply("parent-status-id", "This is a reply")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Or reply to a returned `Status`, including media:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
reply_with_photo = client.reply(
|
|
207
|
+
reply,
|
|
208
|
+
"Replying to my reply",
|
|
209
|
+
media_files=["photo.png"],
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The lower-level equivalent is
|
|
214
|
+
`post_status(..., in_reply_to_id="parent-status-id")`.
|
|
215
|
+
|
|
216
|
+
## Low-level client
|
|
217
|
+
|
|
218
|
+
`TruthSocialClient` can be used directly:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from truthsocial_py import TruthSocialClient
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
with TruthSocialClient(
|
|
225
|
+
client_id="your-client-id",
|
|
226
|
+
client_secret="your-client-secret",
|
|
227
|
+
) as client:
|
|
228
|
+
client.login("username", "password")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
It can also discover the deployed app identity for a single login:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
with TruthSocialClient() as client:
|
|
235
|
+
client.login_with_web_app("username", "password")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Use `discover_web_app_credentials()` to obtain the typed app identity without
|
|
239
|
+
logging in.
|
|
240
|
+
|
|
241
|
+
## Posting behavior
|
|
242
|
+
|
|
243
|
+
Status creation is not retried automatically because a transport failure can
|
|
244
|
+
leave the final outcome unknown. Supply a stable `idempotency_key` when your
|
|
245
|
+
application may retry a post.
|
|
246
|
+
|
|
247
|
+
When `media_files` is used, a later upload or post failure can leave an
|
|
248
|
+
uploaded attachment unused.
|
|
249
|
+
|
|
250
|
+
## Errors
|
|
251
|
+
|
|
252
|
+
All library exceptions inherit from `TruthSocialError`:
|
|
253
|
+
|
|
254
|
+
- `ConfigurationError` and `NotAuthenticatedError`
|
|
255
|
+
- `CredentialDiscoveryError`
|
|
256
|
+
- `AuthenticationError`
|
|
257
|
+
- `RateLimitError`, including an optional `retry_after`
|
|
258
|
+
- `APIError`
|
|
259
|
+
- `NetworkError` and `ProtocolError`
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
Run the mocked test suite:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
PYTHONPATH=src python -m unittest discover -s tests -v
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Live smoke test
|
|
270
|
+
|
|
271
|
+
Prepare the local settings file:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
cp examples/local_credentials.py.example examples/local_credentials.py
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Fill in the username and password, then run:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
python examples/manual_smoke_test.py
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
After confirmation, the script creates a public text post, a public reply, and
|
|
284
|
+
a public image post using `examples/truthsocial-py-test.png`. It does not delete them
|
|
285
|
+
afterward.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# truthsocial-py
|
|
2
|
+
|
|
3
|
+
An unofficial, typed Python client for Truth Social.
|
|
4
|
+
|
|
5
|
+
`truthsocial-py` supports OAuth app discovery, manual app configuration, multiple
|
|
6
|
+
independent user sessions, text and media posts, replies, and structured API
|
|
7
|
+
errors.
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> Truth Social does not publish this interface as a stable public API.
|
|
11
|
+
> Endpoints and payloads may change.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Discover the OAuth app identity from the deployed Truth Social web client
|
|
16
|
+
- Configure an OAuth app identity manually
|
|
17
|
+
- Log in multiple users with isolated tokens, HTTP state, and session IDs
|
|
18
|
+
- Rediscover rotated web-app credentials automatically or on demand
|
|
19
|
+
- Publish text and image posts
|
|
20
|
+
- Reply to a status by ID or `Status` object
|
|
21
|
+
- Reuse existing access tokens
|
|
22
|
+
- Handle authentication, rate-limit, transport, and protocol errors
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Python 3.10 or newer is required.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python -m pip install truthsocial-py
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The distribution is `truthsocial-py`; the import package is `truthsocial_py`:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from truthsocial_py import TruthSocialApp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To work on the library itself:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/Jxck-S/truthsocial-py.git
|
|
42
|
+
cd truthsocial-py
|
|
43
|
+
python3 -m venv .venv
|
|
44
|
+
source .venv/bin/activate
|
|
45
|
+
python -m pip install -e .
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
Create an app from the current Truth Social web deployment, then log in:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import getpass
|
|
54
|
+
|
|
55
|
+
from truthsocial_py import TruthSocialApp
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
app = TruthSocialApp.from_web()
|
|
59
|
+
|
|
60
|
+
with app.login(
|
|
61
|
+
username=input("Truth Social username: "),
|
|
62
|
+
password=getpass.getpass("Truth Social password: "),
|
|
63
|
+
) as client:
|
|
64
|
+
account = client.verify_credentials()
|
|
65
|
+
status = client.post_status("Hello from truthsocial-py!")
|
|
66
|
+
print(f"Posted as @{account.acct}: {status.url}")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## App identities
|
|
70
|
+
|
|
71
|
+
`TruthSocialApp` represents one OAuth application identity. Load the current
|
|
72
|
+
identity from the website:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
app = TruthSocialApp.from_web()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or provide one manually:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
app = TruthSocialApp(
|
|
82
|
+
client_id="your-client-id",
|
|
83
|
+
client_secret="your-client-secret",
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Web-loaded apps enable automatic rediscovery by default. If login returns
|
|
88
|
+
OAuth's `invalid_client` or `unauthorized_client` error, the app refreshes its
|
|
89
|
+
identity and retries once. Refresh it explicitly at any time:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
credentials = app.rediscover()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Rediscovery applies to future clients and login attempts. Existing clients
|
|
96
|
+
keep their current token and app snapshot. A manually configured app can opt
|
|
97
|
+
in with `auto_rediscover=True`.
|
|
98
|
+
|
|
99
|
+
## Multiple users
|
|
100
|
+
|
|
101
|
+
Every login returns a separate `TruthSocialClient`:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
alice = app.login("alice", "alice-password")
|
|
105
|
+
bob = app.login("bob", "bob-password")
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
alice.post_status("Posted by Alice")
|
|
109
|
+
bob.post_status("Posted by Bob")
|
|
110
|
+
finally:
|
|
111
|
+
alice.close()
|
|
112
|
+
bob.close()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Create a client from a previously saved token without logging in again:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
alice = app.new_client(access_token=load_alice_token())
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## User agent
|
|
122
|
+
|
|
123
|
+
Requests are sent with `truthsocial-py/<version>` by default, exposed as
|
|
124
|
+
`truthsocial_py.DEFAULT_USER_AGENT`. Override it on an app or a client:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from truthsocial_py import DEFAULT_USER_AGENT, TruthSocialApp
|
|
128
|
+
|
|
129
|
+
app = TruthSocialApp.from_web(user_agent="my-bot/2.0 (+https://example.com)")
|
|
130
|
+
client = app.new_client() # inherits the app's user agent
|
|
131
|
+
print(client.user_agent)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`TruthSocialClient(..., user_agent=...)` works the same way. The value must be
|
|
135
|
+
a non-empty, header-safe string; anything else raises `ConfigurationError`.
|
|
136
|
+
|
|
137
|
+
## Media posts
|
|
138
|
+
|
|
139
|
+
Pass paths through `media_files`; each file is uploaded before the status:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
status = client.post_status(
|
|
143
|
+
"A photo post",
|
|
144
|
+
media_files=["photo.png"],
|
|
145
|
+
idempotency_key="your-stable-unique-key",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
for attachment in status.media_attachments:
|
|
149
|
+
print(attachment.id, attachment.url)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Upload separately when you need the attachment first:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
attachment = client.upload_media("photo.png")
|
|
156
|
+
status = client.post_status(
|
|
157
|
+
"Uploaded separately",
|
|
158
|
+
media_ids=[attachment.id],
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`upload_media()` also accepts open binary files with optional `filename` and
|
|
163
|
+
`content_type` arguments. Do not combine `media_files` and `media_ids` in the
|
|
164
|
+
same call. Media types and size limits are controlled by Truth Social.
|
|
165
|
+
|
|
166
|
+
Truth Social currently accepts `public` visibility for these posting calls.
|
|
167
|
+
|
|
168
|
+
## Replies
|
|
169
|
+
|
|
170
|
+
Reply using a parent status ID:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
reply = client.reply("parent-status-id", "This is a reply")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or reply to a returned `Status`, including media:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
reply_with_photo = client.reply(
|
|
180
|
+
reply,
|
|
181
|
+
"Replying to my reply",
|
|
182
|
+
media_files=["photo.png"],
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The lower-level equivalent is
|
|
187
|
+
`post_status(..., in_reply_to_id="parent-status-id")`.
|
|
188
|
+
|
|
189
|
+
## Low-level client
|
|
190
|
+
|
|
191
|
+
`TruthSocialClient` can be used directly:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from truthsocial_py import TruthSocialClient
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
with TruthSocialClient(
|
|
198
|
+
client_id="your-client-id",
|
|
199
|
+
client_secret="your-client-secret",
|
|
200
|
+
) as client:
|
|
201
|
+
client.login("username", "password")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
It can also discover the deployed app identity for a single login:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
with TruthSocialClient() as client:
|
|
208
|
+
client.login_with_web_app("username", "password")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use `discover_web_app_credentials()` to obtain the typed app identity without
|
|
212
|
+
logging in.
|
|
213
|
+
|
|
214
|
+
## Posting behavior
|
|
215
|
+
|
|
216
|
+
Status creation is not retried automatically because a transport failure can
|
|
217
|
+
leave the final outcome unknown. Supply a stable `idempotency_key` when your
|
|
218
|
+
application may retry a post.
|
|
219
|
+
|
|
220
|
+
When `media_files` is used, a later upload or post failure can leave an
|
|
221
|
+
uploaded attachment unused.
|
|
222
|
+
|
|
223
|
+
## Errors
|
|
224
|
+
|
|
225
|
+
All library exceptions inherit from `TruthSocialError`:
|
|
226
|
+
|
|
227
|
+
- `ConfigurationError` and `NotAuthenticatedError`
|
|
228
|
+
- `CredentialDiscoveryError`
|
|
229
|
+
- `AuthenticationError`
|
|
230
|
+
- `RateLimitError`, including an optional `retry_after`
|
|
231
|
+
- `APIError`
|
|
232
|
+
- `NetworkError` and `ProtocolError`
|
|
233
|
+
|
|
234
|
+
## Development
|
|
235
|
+
|
|
236
|
+
Run the mocked test suite:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
PYTHONPATH=src python -m unittest discover -s tests -v
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Live smoke test
|
|
243
|
+
|
|
244
|
+
Prepare the local settings file:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
cp examples/local_credentials.py.example examples/local_credentials.py
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Fill in the username and password, then run:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
python examples/manual_smoke_test.py
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
After confirmation, the script creates a public text post, a public reply, and
|
|
257
|
+
a public image post using `examples/truthsocial-py-test.png`. It does not delete them
|
|
258
|
+
afterward.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "truthsocial-py"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "An unofficial typed Python client for Truth Social"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Jack Sweeney", email = "jackhsweeney1@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"httpx>=0.27,<1",
|
|
18
|
+
]
|
|
19
|
+
keywords = ["truth-social", "truthsocial", "api", "client", "mastodon"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Internet",
|
|
30
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
31
|
+
"Typing :: Typed",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/Jxck-S/truthsocial-py"
|
|
36
|
+
Repository = "https://github.com/Jxck-S/truthsocial-py"
|
|
37
|
+
Issues = "https://github.com/Jxck-S/truthsocial-py/issues"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.dynamic]
|
|
40
|
+
version = { attr = "truthsocial_py._version.__version__" }
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.package-data]
|
|
46
|
+
truthsocial_py = ["py.typed"]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from ._version import __version__
|
|
2
|
+
from .app import TransportFactory, TruthSocialApp
|
|
3
|
+
from .client import (
|
|
4
|
+
DEFAULT_BASE_URL,
|
|
5
|
+
DEFAULT_SCOPE,
|
|
6
|
+
DEFAULT_USER_AGENT,
|
|
7
|
+
MediaSource,
|
|
8
|
+
OOB_REDIRECT_URI,
|
|
9
|
+
TruthSocialClient,
|
|
10
|
+
)
|
|
11
|
+
from .errors import (
|
|
12
|
+
APIError,
|
|
13
|
+
AuthenticationError,
|
|
14
|
+
ConfigurationError,
|
|
15
|
+
CredentialDiscoveryError,
|
|
16
|
+
NetworkError,
|
|
17
|
+
NotAuthenticatedError,
|
|
18
|
+
ProtocolError,
|
|
19
|
+
RateLimitError,
|
|
20
|
+
TruthSocialError,
|
|
21
|
+
)
|
|
22
|
+
from .models import (
|
|
23
|
+
Account,
|
|
24
|
+
MediaAttachment,
|
|
25
|
+
OAuthAppCredentials,
|
|
26
|
+
OAuthToken,
|
|
27
|
+
Status,
|
|
28
|
+
Visibility,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"APIError",
|
|
33
|
+
"Account",
|
|
34
|
+
"AuthenticationError",
|
|
35
|
+
"ConfigurationError",
|
|
36
|
+
"CredentialDiscoveryError",
|
|
37
|
+
"DEFAULT_BASE_URL",
|
|
38
|
+
"DEFAULT_SCOPE",
|
|
39
|
+
"DEFAULT_USER_AGENT",
|
|
40
|
+
"MediaAttachment",
|
|
41
|
+
"MediaSource",
|
|
42
|
+
"NetworkError",
|
|
43
|
+
"NotAuthenticatedError",
|
|
44
|
+
"OAuthToken",
|
|
45
|
+
"OAuthAppCredentials",
|
|
46
|
+
"OOB_REDIRECT_URI",
|
|
47
|
+
"ProtocolError",
|
|
48
|
+
"RateLimitError",
|
|
49
|
+
"Status",
|
|
50
|
+
"TruthSocialClient",
|
|
51
|
+
"TruthSocialApp",
|
|
52
|
+
"TruthSocialError",
|
|
53
|
+
"TransportFactory",
|
|
54
|
+
"Visibility",
|
|
55
|
+
"__version__",
|
|
56
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|