Skip to content

Commit 1d4de86

Browse files
committed
categories methods
1 parent 58fc5e9 commit 1d4de86

2 files changed

Lines changed: 81 additions & 26 deletions

File tree

substack/api.py

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ class Api:
1515
"""
1616

1717
def __init__(
18-
self,
19-
email: str,
20-
password: str,
21-
base_url: str | None = None,
22-
publication_url: str | None = None,
23-
debug: bool = False,
18+
self,
19+
email: str | None = None,
20+
password: str | None = None,
21+
base_url: str | None = None,
22+
publication_url: str | None = None,
23+
debug: bool = False,
2424
):
2525
"""
2626
@@ -42,14 +42,17 @@ def __init__(
4242
logging.basicConfig()
4343
logging.getLogger().setLevel(logging.DEBUG)
4444

45-
self._init_session(email, password)
45+
self._session = requests.Session()
46+
47+
if email is not None and password is not None:
48+
self.login(email, password)
4649

4750
def login(self, email: str, password: str) -> dict:
4851
"""
4952
5053
Args:
51-
email:
52-
password:
54+
email: substack account email
55+
password: substack account password
5356
"""
5457

5558
response = self._session.post(
@@ -64,11 +67,6 @@ def login(self, email: str, password: str) -> dict:
6467
)
6568
return Api._handle_response(response=response)
6669

67-
def _init_session(self, email, password):
68-
self._session = requests.Session()
69-
70-
self.login(email, password)
71-
7270
@staticmethod
7371
def _handle_response(response: requests.Response):
7472
"""
@@ -112,11 +110,11 @@ def get_drafts(self, filter: str = None, offset: int = None, limit: int = None):
112110
return Api._handle_response(response=response)
113111

114112
def post_draft(
115-
self,
116-
draft_bylines: list,
117-
title: str = None,
118-
subtitle: str = None,
119-
body: str = None,
113+
self,
114+
draft_bylines: list,
115+
title: str = None,
116+
subtitle: str = None,
117+
body: str = None,
120118
) -> dict:
121119
"""
122120
@@ -141,12 +139,12 @@ def post_draft(
141139
return Api._handle_response(response=response)
142140

143141
def put_draft(
144-
self,
145-
draft: str,
146-
title: str = None,
147-
subtitle: str = None,
148-
body: str = None,
149-
cover_image: str = None,
142+
self,
143+
draft: str,
144+
title: str = None,
145+
subtitle: str = None,
146+
body: str = None,
147+
cover_image: str = None,
150148
) -> dict:
151149
"""
152150
@@ -188,7 +186,7 @@ def prepublish_draft(self, draft: str) -> dict:
188186
return Api._handle_response(response=response)
189187

190188
def publish_draft(
191-
self, draft: str, send: bool = True, share_automatically: bool = False
189+
self, draft: str, send: bool = True, share_automatically: bool = False
192190
) -> dict:
193191
"""
194192
@@ -205,3 +203,50 @@ def publish_draft(
205203
json={"send": send, "share_automatically": share_automatically},
206204
)
207205
return Api._handle_response(response=response)
206+
207+
def get_categories(self):
208+
"""
209+
210+
Retrieve list of all available categories.
211+
212+
Returns:
213+
214+
"""
215+
response = self._session.get(f"{self.base_url}/categories")
216+
return Api._handle_response(response=response)
217+
218+
def get_category(self, category_id: int, category_type: str, page: int):
219+
response = self._session.get(f"{self.base_url}/category/public/{category_id}/{category_type}",
220+
params={"page": page})
221+
return Api._handle_response(response=response)
222+
223+
def get_single_category(self, category_id: int, category_type: str, page: int | None = None,
224+
limit: int | None = None):
225+
"""
226+
227+
Args:
228+
category_id:
229+
category_type: paid or all
230+
page: by default substack retrieves only the first 25 publications in the category. If this is left None,
231+
then all pages will be retrieved. The page size is 25 publications.
232+
limit:
233+
Returns:
234+
235+
"""
236+
if page is not None:
237+
output = self.get_category(category_id, category_type, page)
238+
else:
239+
publications = []
240+
page = 0
241+
while True:
242+
page_output = self.get_category(category_id, category_type, page)
243+
publications.extend(page_output.get("publications", []))
244+
if (limit is not None and limit <= len(publications)) or not page_output.get("more", False):
245+
publications = publications[:limit]
246+
break
247+
page += 1
248+
output = {
249+
"publications": publications,
250+
"more": page_output.get("more", False)
251+
}
252+
return output

tests/substack/test_api.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,13 @@ def test_put_draft(self):
6262
)
6363
posted_draft = api.put_draft("")
6464
self.assertIsNotNone(posted_draft)
65+
66+
def test_get_categories(self):
67+
api = Api()
68+
categories = api.get_categories()
69+
self.assertIsNotNone(categories)
70+
71+
def test_get_single_category(self):
72+
api = Api()
73+
category = api.get_single_category(4, "all", limit=100)
74+
self.assertIsNotNone(category)

0 commit comments

Comments
 (0)