-
-
Save hogseedy/b149c5f1ad1b628ba00556c7d4a898f8 to your computer and use it in GitHub Desktop.
Reddit native auth / GQL example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
import hashlib | |
import base64 | |
import uuid | |
import time | |
import json | |
import hmac | |
# Android app api keys, posted here: https://news.ycombinator.com/item?id=36086240 | |
REDDIT_ANDROID_OAUTH_CLIENT_ID = 'ohXpoqrZYub1kg' | |
REDDIT_ANDROID_HMAC_SECRET = '8c7abaa5f905f70400c81bf3a1a101e75f7210104b1991f0cd5240aa80c4d99d' | |
class Reddit: | |
GQL_ENDPOINT = 'https://gql.reddit.com' | |
AUTH_ENDPOINT = 'https://accounts.reddit.com' | |
USER_AGENT = 'Reddit/Version 2023.21.0/Build 956283/Android 13' | |
def __init__(self, client_id, hmac_secret): | |
self.client_id = client_id | |
self.hmac_secret = hmac_secret | |
self.token = None | |
self.s = requests.session() | |
self.uuid = str(uuid.uuid4()) | |
self.s.headers.update({ | |
'Client-Vendor-Id': self.uuid, | |
'X-Reddit-Device-Id': self.uuid, | |
'User-Agent': self.USER_AGENT | |
}) | |
def login_anonymous(self): | |
return self._get_token() | |
def login_user(self, username, password): | |
epoch = str(int(time.time())) | |
body = json.dumps({'username': username, 'password': password}, separators = (',', ':')) | |
body_sig = f'1:android:2:{epoch}:{self._generate_body_sig(epoch, body)}' | |
result_sig = f'1:android:2:{epoch}:{self._generate_result_sig(epoch)}' | |
r = self.s.post( | |
f'{self.AUTH_ENDPOINT}/api/login', | |
data = body, | |
headers = { | |
'X-Hmac-Signed-Body': body_sig, | |
'X-Hmac-Signed-Result': result_sig | |
} | |
) | |
if r.json()['success']: | |
print('reddit_session =', r.cookies['reddit_session']) | |
print('modhash =', r.json()['modhash']) | |
return self._get_token() | |
else: | |
raise Exception('Login error') | |
def gql_get_posts(self, subreddit, sort='HOT', after=None): | |
if not self.token: | |
raise Exception('Not authorized') | |
json_body = { | |
'id': '3496a5858eb9', # SubredditFeedElements | |
'variables': { | |
'subredditName': subreddit, | |
'sort': sort, | |
'adContext': { | |
'distance': None, | |
'layout': 'CARD', | |
'deviceAdId': '', | |
'clientSignalSessionData': { | |
'adsSeenCount': 123, | |
'totalPostsSeenCount': 456, | |
'sessionStartTime': '2023-06-05T12:34:56.000000+0000' | |
} | |
}, | |
'forceAds': {}, | |
'feedFilters': {}, | |
'optedIn': True, | |
'includeSubredditInPosts': False, | |
'includeAwards': False, | |
'feedContext': { | |
'experimentOverrides': [] | |
}, | |
'includePostStats': False | |
} | |
} | |
if after: | |
json_body['variables']['after'] = base64.b64encode(after.encode()).decode() | |
r = self.s.post( | |
self.GQL_ENDPOINT, | |
json = json_body | |
) | |
return r.json() | |
def _get_token(self): | |
r = self.s.post( | |
f'{self.AUTH_ENDPOINT}/api/access_token', | |
json = { | |
'scopes': ['*','email','pii'] | |
}, | |
auth = requests.auth.HTTPBasicAuth(self.client_id, '') | |
) | |
if 'access_token' in r.json(): | |
self.token = r.json()['access_token'] | |
self.s.headers.update({'Authorization': f'Bearer {self.token}'}) | |
return True | |
else: | |
raise Exception('Login error') | |
def _generate_body_sig(self, epoch, body): | |
return self._hmac_sign_data(f'Epoch:{epoch}|Body:{body}') | |
def _generate_result_sig(self, epoch): | |
return self._hmac_sign_data(f'Epoch:{epoch}|User-Agent:{self.USER_AGENT}|Client-Vendor-ID:{self.uuid}') | |
def _hmac_sign_data(self, data): | |
return hmac.new(self.hmac_secret.encode(), msg = data.encode(), digestmod = hashlib.sha256).hexdigest() | |
if __name__ == '__main__': | |
r = Reddit(REDDIT_ANDROID_OAUTH_CLIENT_ID, REDDIT_ANDROID_HMAC_SECRET) | |
r.login_anonymous() # r.login_user('user', 'pass') | |
print(r.gql_get_posts('technology', after='t3_13zvyn5')) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is awesome, thank you. Is there a way to save the session cookies and load it to use it after?