Skip to content

Commit e0743f9

Browse files
authored
Allow saving / loading of auth cookies. (#25)
1 parent e3235b8 commit e0743f9

File tree

1 file changed

+46
-22
lines changed

1 file changed

+46
-22
lines changed

substack/api.py

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
from datetime import datetime
55
from urllib.parse import urljoin
6+
import json
67

78
import requests
89

@@ -24,6 +25,7 @@ def __init__(
2425
self,
2526
email=None,
2627
password=None,
28+
cookies_path=None,
2729
base_url=None,
2830
publication_url=None,
2931
debug=False,
@@ -37,6 +39,9 @@ def __init__(
3739
Args:
3840
email:
3941
password:
42+
cookies_path
43+
To re-use your session without logging in each time, you can save your cookies to a json file and then load them in the next session.
44+
Make sure to re-save your cookies, as they do update over time.
4045
base_url:
4146
The base URL to use to contact the Substack API.
4247
Defaults to https://substack.com/api/v1.
@@ -49,30 +54,39 @@ def __init__(
4954

5055
self._session = requests.Session()
5156

52-
if email is not None and password is not None:
57+
# Load cookies from file if provided
58+
# Helps with Captcha errors by reusing cookies from "local" auth, then switching to running code in the cloud
59+
if cookies_path is not None:
60+
with open(cookies_path, "r") as f:
61+
cookies = json.load(f)
62+
self._session.cookies.update(cookies)
63+
64+
elif email is not None and password is not None:
5365
self.login(email, password)
66+
else:
67+
raise ValueError("Must provide email and password or cookies_path to authenticate.")
68+
69+
# if the user provided a publication url, then use that
70+
if publication_url:
71+
import re
72+
73+
# Regular expression to extract subdomain name
74+
match = re.search(r"https://(.*).substack.com", publication_url.lower())
75+
subdomain = match.group(1) if match else None
76+
77+
user_publications = self.get_user_publications()
78+
# search through publications to find the publication with the matching subdomain
79+
for publication in user_publications:
80+
if publication['subdomain'] == subdomain:
81+
# set the current publication to the users publication
82+
user_publication = publication
83+
break
84+
else:
85+
# get the users primary publication
86+
user_publication = self.get_user_primary_publication()
5487

55-
# if the user provided a publication url, then use that
56-
if publication_url:
57-
import re
58-
59-
# Regular expression to extract subdomain name
60-
match = re.search(r"https://(.*).substack.com", publication_url.lower())
61-
subdomain = match.group(1) if match else None
62-
63-
user_publications = self.get_user_publications()
64-
# search through publications to find the publication with the matching subdomain
65-
for publication in user_publications:
66-
if publication['subdomain'] == subdomain:
67-
# set the current publication to the users publication
68-
user_publication = publication
69-
break
70-
else:
71-
# get the users primary publication
72-
user_publication = self.get_user_primary_publication()
73-
74-
# set the current publication to the users primary publication
75-
self.change_publication(user_publication)
88+
# set the current publication to the users primary publication
89+
self.change_publication(user_publication)
7690

7791
def login(self, email, password) -> dict:
7892
"""
@@ -114,6 +128,16 @@ def change_publication(self, publication):
114128
# sign-in to the publication
115129
self.signin_for_pub(publication)
116130

131+
def export_cookies(self, path: str = "cookies.json"):
132+
"""
133+
Export cookies to a json file.
134+
Args:
135+
path: path to the json file
136+
"""
137+
cookies = self._session.cookies.get_dict()
138+
with open(path, "w") as f:
139+
json.dump(cookies, f)
140+
117141
@staticmethod
118142
def _handle_response(response: requests.Response):
119143
"""

0 commit comments

Comments
 (0)