-
-
Save Romern/225ceeca7a7825c0d2be7554c03b2bea to your computer and use it in GitHub Desktop.
import requests | |
import json | |
import os | |
from datetime import datetime | |
baseurl = "https://api.studydrive.net/" | |
def login(user, passwd): | |
param = {"client_id": 4, | |
"client_secret": "nmGaT4rJ3VVGQXu75ymi5Cu5bdqb3tFnkWw9f1IX", | |
"grant_type":"password", | |
"username": user, | |
"password": passwd} | |
req = requests.post('{}oauth/token'.format(baseurl), data=param) | |
req.raise_for_status() | |
return json.loads(req.text)['access_token'] | |
def getTime(): | |
return str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) | |
def getUniversityData(universityid, token): #returns all courses of the university | |
headers={"authorization": "Bearer "+token} | |
req = requests.get('{}api/app/v1/universities/{}/courses'.format(baseurl,universityid), headers=headers) | |
req.raise_for_status() | |
return json.loads(req.text) | |
def getCourseData(courseid, token, page=0, reference_time=None): | |
#reference_time="2019-09-24 14:52:08" | |
param = {"sort": "time", | |
"page": page, | |
"semester_from":0, | |
"semester_until":0, | |
"type_ids":0} | |
if page>0: | |
if reference_time is None: | |
reference_time = getTime() | |
param["reference_time"] = reference_time | |
headers={"authorization": "Bearer "+token} | |
req = requests.get('{}api/app/v1/feed/courses/{}/documents'.format(baseurl,courseid), params=param, headers=headers) | |
req.raise_for_status() | |
return json.loads(req.text) | |
def getFullCourseData(courseid, token, until=None): # until is of type date, e.g. datetime.now() | |
reference_time = getTime() | |
init_data = getCourseData(courseid, token, page=0, reference_time=reference_time) | |
last_page = int(init_data["last_page"]) | |
files = init_data["files"] | |
for i in range(1,last_page+1): | |
if (until != None) and (len([f for f in files if datetime.strptime(f["uploaded"], '%Y-%m-%d %H:%M:%S')>until])==0): | |
break | |
files.extend(getCourseData(courseid, token, page=i, reference_time=reference_time)["files"]) | |
init_data["files"] = files | |
return init_data | |
def getDocument(docid, token): | |
headers={"authorization": "Bearer "+token} | |
#uploadDate = datetime.strptime(data["files"][0]["uploaded"], '%Y-%m-%d %H:%M:%S') | |
req = requests.get('{}api/app/v1/documents/{}/download'.format(baseurl,docid), headers=headers) | |
req.raise_for_status() | |
return req.content | |
def downloadAllFilesInCourse(filelist, token, folder="."): | |
for f in filelist: | |
docid = f['file_id'] | |
docname = f['file_name'] + docid + f["file_name"].split(".")[-1] | |
print("Downloading {}...".format(docname)) | |
doc = getDocument(docid,login_token) | |
if os.path.isfile(docname): | |
print("Found duplicate: {} already exists".format(docname)) | |
file = open(folder + "/" + docname, "wb") | |
file.write(doc) | |
file.close() | |
def crawlAllCourses(lastcrawled, university_id, token): | |
#lastcrawled = {'50936': "2019-09-27 11:38:57", ...} | |
courses = getUniversityData(universityid, token) | |
for c in courses: | |
if c["course_id"] in lastcrawled.keys(): | |
until = lastcrawled[c["course_id"]] | |
else: | |
until = None | |
data = getFullCourseData(c["course_id"], token, until=until) | |
if not os.path.exists(c["course_name"]): | |
os.mkdir(c["course_name"]) | |
downloadAllFilesInCourse(data["files"], token, folder=c["course_name"]) | |
lastcrawled[c["course_id"]] = data["files"][0]["uploaded"] | |
return lastcrawled #return updated lastcrawled |
@henrydatei I have just sniffed the traffic of the app. You could probably also just decompile the apk using apktool for example, a list of the apis is in base.apk/smali_classes3/de/veedapp/veed/network/StudydriveApiInterface.smali :
/api/app/v1/{answer_type}/answers/{answer_id}/create
/api/app/v1/{answer_type}/answers/{answer_id}/delete
/api/app/v1/{answer_type}/answers/{answer_id}/downvote
/api/app/v1/{answer_type}/answers/{answer_id}/edit
/api/app/v1/{answer_type}/answers/{answer_id}/report
/api/app/v1/{answer_type}/answers/{answer_id}/upload
/api/app/v1/{answer_type}/answers/{answer_id}/upload/{file_id}/delete
/api/app/v1/{answer_type}/answers/{answer_id}/upvote
/api/app/v1/community/order
/api/app/v1/courses/{course_id}/join
/api/app/v1/courses/{course_id}/leave
/api/app/v1/deeplink/{type}/{related_id}/data
/api/app/v1/documents/{file_id}/delete
/api/app/v1/documents/{file_id}/details
/api/app/v1/documents/{file_id}/details
/api/app/v1/documents/{file_id}/downvote
/api/app/v1/documents/{file_id}/edit
/api/app/v1/documents/{file_id}/follow
/api/app/v1/documents/{file_id}/increment
/api/app/v1/documents/{file_id}/mute
/api/app/v1/documents/{file_id}/report
/api/app/v1/documents/{file_id}/storage-url
/api/app/v1/documents/{file_id}/upvote
/api/app/v1/documents/upload
/api/app/v1/documents/upload/init
/api/app/v1/documents/upload/{upload_hash}/cancel
/api/app/v1/documents/upload/{upload_hash}/finalize
/api/app/v1/feed/courses/{course_id}/discussion
/api/app/v1/feed/courses/{course_id}/documents
/api/app/v1/feed/courses/{course_id}/documents/filter
/api/app/v1/feed/courses/{course_id}/files
/api/app/v1/feed/flashcards/sets
/api/app/v1/feed/flashcards/{user_id}/sets
/api/app/v1/feed/groups/{group_id}/discussion
/api/app/v1/feed/header
/api/app/v1/feed/header/read
/api/app/v1/feed/header/unread
/api/app/v1/feed/my_answers_feed
/api/app/v1/feed/my_documents
/api/app/v1/feed/my_favored_questions_feed
/api/app/v1/feed/my_flashcard_sets
/api/app/v1/feed/my_followed_documents
/api/app/v1/feed/my_followed_flashcard_sets
/api/app/v1/feed/my_followed_users
/api/app/v1/feed/my_questions_feed
/api/app/v1/feed/newsfeed
/api/app/v1/flashcards/{id}/bookmark
/api/app/v1/flashcards/{id}/rate
/api/app/v1/flashcards/image/add
/api/app/v1/flashcards/sets/create
/api/app/v1/flashcards/sets/{id}
/api/app/v1/flashcards/sets/{id}/delete
/api/app/v1/flashcards/sets/{id}/follow
/api/app/v1/flashcards/sets/{id}/rate/reset
/api/app/v1/flashcards/sets/{id}/report
/api/app/v1/flashcards/sets/{id}/study/finish
/api/app/v1/flashcards/sets/{id}/study/start
/api/app/v1/flashcards/sets/{id}/update
/api/app/v1/flashcards/sets/{id}/vote/down
/api/app/v1/flashcards/sets/{id}/vote/up
/api/app/v1/forgot-password
/api/app/v1/groups
/api/app/v1/groups/{group_id}/faq
/api/app/v1/groups/{group_id}/join
/api/app/v1/groups/{group_id}/leave
/api/app/v1/groups/{group_id}/mute
/api/app/v1/groups/{group_id}/unmute
/api/app/v1/groups/suggest
/api/app/v1/groups/target
/api/app/v1/html/{content}
/api/app/v1/ke/accept
/api/app/v1/ke/dashboard
/api/app/v1/ke/decline
/api/app/v1/ke/earnings
/api/app/v1/ke/goal/{id}
/api/app/v1/ke/small
/api/app/v1/login
/api/app/v1/logout
/api/app/v1/majors
/api/app/v1/majors/subscribe
/api/app/v1/mixpanel/enabled
/api/app/v1/myself
/api/app/v1/myself
/api/app/v1/myself/courses
/api/app/v1/myself/groups
/api/app/v1/onboarding/feed
/api/app/v1/{poll_type}/questions/poll/{poll_id}/vote
/api/app/v1/profiles/picture/delete
/api/app/v1/profiles/studies/reset
/api/app/v1/profiles/{user_id}
/api/app/v1/profiles/{user_id}/documents
/api/app/v1/profiles/{user_id}/follow
/api/app/v1/push-notification/subscribe
/api/app/v1/push-notification/unsubscribe
/api/app/v1/questions/downvote
/api/app/v1/questions/upvote
/api/app/v1/{question_type}/questions/init
/api/app/v1/{question_type}/questions/{question_id}
/api/app/v1/{question_type}/questions/{question_id}/answers/init
/api/app/v1/{question_type}/questions/{question_id}/best_answer
/api/app/v1/{question_type}/questions/{question_id}/create
/api/app/v1/{question_type}/questions/{question_id}/create
/api/app/v1/{question_type}/questions/{question_id}/delete
/api/app/v1/{question_type}/questions/{question_id}/edit
/api/app/v1/{question_type}/questions/{question_id}/favor
/api/app/v1/{question_type}/questions/{question_id}/mute
/api/app/v1/{question_type}/questions/{question_id}/report
/api/app/v1/{question_type}/questions/{question_id}/upload
/api/app/v1/{question_type}/questions/{question_id}/upload/{file_id}/delete
/api/app/v1/rewards
/api/app/v1/rewards/order
/api/app/v1/search/{context}
/api/app/v1/semester
/api/app/v1/semester
/api/app/v1/universities
/api/app/v1/universities/create
/api/app/v1/universities/subscribe
/api/app/v1/universities/{university_id}/courses
/api/app/v1/universities/{university_id}/courses/create
/api/app/v1/universities/{university_id}/degree_program
/api/app/v1/universities/{university_id}/degree_program/create
/api/app/v1/universities/{university_id}/degree_program/degree_types
/api/app/v1/universities/{university_id}/degree_program/subscribe
/api/app/v1/users/credits/detail
/api/app/v1/users/delete
/api/app/v1/users/email
/api/app/v1/users/email/check
/api/app/v1/users/feedback
/api/app/v1/users/feedback
/api/app/v1/users/gender
/api/app/v1/users/give-karma
/api/app/v1/users/left_sidebar
/api/app/v1/users/majors
/api/app/v1/users/nickname
/api/app/v1/users/password
/api/app/v1/users/register/email
/api/app/v1/users/right_sidebar_stats
/api/app/v1/users/settings
/api/app/v1/users/settings
/api/app/v1/users/upload_profile_picture
/api/app/v1/users/verify/email
/api/app/v1/verification/resend_email
Thank you very much. I had the plan decompiling the app today but now that is not necessary.
Do you know how
‘‘‘
/api/app/v1/{answer_type}/answers/{answer_id}/upvote
‘‘‘
works? The answer_type is the same as the question_type (document, course) and the answer_id should be clear. But if I try a POST request with the parameter token given when logging in, I get an error („Something gone wrong“, very helpful 😂)
no idea, works for me. Maybe you didnt specify it is a POST request and it tries GET?
curl -X POST -H "authorization: Bearer ********" https://api.studydrive.net/api/app/v1/document/answers/*****/upvote
{"success":true,"rating":1,"hasvotes":true,"upvotes":1,"downvotes":0,"uservote":"up"}%
Thanks for your fast answer - for testing purposes I used curl instead of python and forgot the -X POST 🙈
is there an official documentation for the API? How do you know the functions of the API?