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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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"