Skip to content

Instantly share code, notes, and snippets.

@hogseedy
Last active November 21, 2024 03:45
Show Gist options
  • Save hogseedy/b149c5f1ad1b628ba00556c7d4a898f8 to your computer and use it in GitHub Desktop.
Save hogseedy/b149c5f1ad1b628ba00556c7d4a898f8 to your computer and use it in GitHub Desktop.
Reddit native auth / GQL example
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'))
@prime-optimal
Copy link

Thanks for this!

@juanpnvd
Copy link

juanpnvd commented Jul 5, 2023

This is awesome, thank you. Is there a way to save the session cookies and load it to use it after?

@JackSparrowf9
Copy link

does it still work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment