diff --git a/.gitignore b/.gitignore index ff3882a..faa3124 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -parler/__pycache__/ \ No newline at end of file +parler/__pycache__/ +.idea/ +*.pyc diff --git a/parler/authentication.py b/parler/authentication.py index a46a8b2..4cedfcd 100644 --- a/parler/authentication.py +++ b/parler/authentication.py @@ -1,30 +1,31 @@ import uuid + class AuthenticationAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url self.s = session def add_password_to_account(self, password, passwordConfirm): - ''' + """ POST /v1/login/passwordSetup params: password - passwordConfirm + passwordConfirm response: 400: {"message": "This person already has a password set, and can not be set through this API"} rate-limit: 5 auth: yes - ''' + """ return self.s.post( - "{}/v1/login/passwordSetup".format(self.root_url), + "{}/v1/login/passwordSetup".format(self.root_url), data={ 'password': password, 'passwordConfirm': passwordConfirm }) def authenticate(self, email, password, captchaToken, captchaValue, deviceId=None): - ''' + """ POST /v1/login params: email @@ -37,12 +38,12 @@ class AuthenticationAPI: response: rate-limit: 5 auth: no - ''' + """ if deviceId is None: deviceId = str(uuid.uuid4()) return self.s.post( - "{}/v1/login".format(self.root_url), + "{}/v1/login".format(self.root_url), data={ 'email': email, 'password': password, @@ -52,19 +53,19 @@ class AuthenticationAPI: }) def begin_registration_otp(self): - ''' - GET /v1/login/otp/registration + """ + GET /v1/login/otp/registration returns OTP token as base64 QR code and secret response: 200: {"message": "NEED_REG", "registration": "data:image/png;base64,...", "secret": "O5ZW..."} 200: {"message": "OK"} - if already added rate-limit: 5 auth: yes - ''' + """ return self.s.get("{}/v1/login/otp/registration".format(self.root_url)) def change_password(self, password, new_password): - ''' + """ PATCH /v1/profile params: passwordOld @@ -74,26 +75,26 @@ class AuthenticationAPI: 400: { "message": "The provided name and password provided do not match", "fieldvalidationsMap": [] } rate-limit: 4 auth: yes - ''' + """ return self.s.patch( - "{}/v1/profile".format(self.root_url), + "{}/v1/profile".format(self.root_url), data={ 'passwordOld': password, 'passwordNew': new_password, 'passwordConfirmation': new_password }) - + def checkin(self): - ''' + """ GET /v1/checkin removed - ''' + """ return self.s.get( "{}/v1/checkin" ) def create_account(self, email, password, captchaToken, captchaValue, deviceId=None): - ''' + """ POST /v1/login/register removed, replaced by V2 API params: @@ -108,12 +109,12 @@ class AuthenticationAPI: captchaValue response: 404: replaced by V2 Auth API - ''' + """ if deviceId is None: deviceId = str(uuid.uuid4()) return self.s.post( - "{}/v1/login/register".format(self.root_url), + "{}/v1/login/register".format(self.root_url), data={ 'email': email, 'password': password, @@ -124,35 +125,35 @@ class AuthenticationAPI: }) def disable_otp(self, totp): - ''' - POST /v1/login/otp/deregistration + """ + POST /v1/login/otp/deregistration params: totp response: 403: {"message": "Invalid OTP code provided, please try again"} rate-limit: 5 auth: yes - ''' + """ return self.s.post( - "{}/v1/login/otp/deregistration".format(self.root_url), + "{}/v1/login/otp/deregistration".format(self.root_url), data={ 'totp': totp }) def email_use_status(self, email): - ''' + """ GET /v1/login/{email} response: 200: {"message": "HAS_PASSWORD"} rate-limit: 5 auth: no - ''' + """ return self.s.get( "{}/v1/login/{}".format(self.root_url, email) ) def enroll_otp(self, totp, secret): - ''' + """ POST /v1/login/otp/registration params: totp @@ -161,32 +162,32 @@ class AuthenticationAPI: 200: {"message": "OK"} rate-limit: 5 auth: yes - ''' + """ return self.s.post( - "{}/v1/login/otp/registration".format(self.root_url), + "{}/v1/login/otp/registration".format(self.root_url), data={ 'totp': totp, 'secret': secret }) def get_a_captcha(self): - ''' + """ GET /v1/login/captcha base64 png response: 200: {"captchaToken": "00cd2762aeea4bc3ae93f504d739de88", "data": "/9j/2wBDAAY..."} rate-limit: 6 auth: no - ''' + """ return self.s.get("{}/v1/login/captcha".format(self.root_url)) def logout(self, authenticationToken): - ''' - POST /v1/logout - params: + """ + POST /v1/logout + params: authenticationToken auth: yes - ''' + """ return self.s.post( "{}/v1/logout".format(self.root_url), data={ @@ -195,7 +196,7 @@ class AuthenticationAPI: ) def request_password_reset(self, email): - ''' + """ POST /v1/login/resetReqeust params: email @@ -203,7 +204,7 @@ class AuthenticationAPI: 200: {"message": "OK"} rate-limit: 3 auth: no - ''' + """ return self.s.post( "{}/v1/login/resetRequest".format(self.root_url), data={ @@ -212,7 +213,7 @@ class AuthenticationAPI: ) def request_signin_link_for_email(self, email, captchaToken, captchaValue, deviceId): - ''' + """ POST /v1/signin/request removed params: @@ -222,7 +223,7 @@ class AuthenticationAPI: captchaValue response: 404: {"message": "No resource for URL", "fieldvalidationsMap": []}} - ''' + """ if deviceId is None: deviceId = str(uuid.uuid4()) @@ -237,7 +238,7 @@ class AuthenticationAPI: ) def reset_user_password(self, password, passwordConfirm, resetToken): - ''' + """ POST /v2/login/password/reset/submit params: password @@ -247,10 +248,10 @@ class AuthenticationAPI: 400: {"message": "Verification code expired", "fieldvalidationsMap": [] } rate-limit: 4 auth: no - ''' + """ return self.s.post( "{}/v2/login/password/reset/submit".format(self.root_url), - data = { + data={ 'password': password, 'passwordConfirm': passwordConfirm, 'resetToken': resetToken @@ -258,7 +259,7 @@ class AuthenticationAPI: ) def send_email_reminder(self, username): - ''' + """ POST /v1/login/reminder sends email reminder for username params: @@ -267,7 +268,7 @@ class AuthenticationAPI: 200: {"message": "success"} rate-limit: 15 auth: no - ''' + """ return self.s.post( "{}/v1/login/reminder".format(self.root_url), data={ @@ -276,7 +277,7 @@ class AuthenticationAPI: ) def send_notification_token(self, notificationId): - ''' + """ POST /v1/notification Mobile push notification token params: @@ -285,16 +286,16 @@ class AuthenticationAPI: 200: {"message": "success"} rate-limit: 5 auth: yes - ''' + """ return self.s.post( "{}/v1/notification".format(self.root_url), - data = { + data={ 'notificationId': notificationId } ) def sign_in_with_email(self, email, code, deviceId=None): - ''' + """ POST /v1/signin sign in using code sent to email. removed params: @@ -306,7 +307,7 @@ class AuthenticationAPI: notificationId response: 404: { "message": "No resource for URL", "fieldvalidationsMap": [] } - ''' + """ if deviceId is None: deviceId = str(uuid.uuid4()) @@ -320,8 +321,8 @@ class AuthenticationAPI: ) def sign_up_email(self, email, name, deviceId=None): - ''' - POST /v1/signup + """ + POST /v1/signup removed params: email @@ -329,7 +330,7 @@ class AuthenticationAPI: deviceId response: 404: { "message": "No resource for URL", "fieldvalidationsMap": [] } - ''' + """ if deviceId is None: deviceId = str(uuid.uuid4()) @@ -343,7 +344,7 @@ class AuthenticationAPI: ) def verify_reset_code(self, token): - ''' + """ POST /v2/login/password/reset/verify params: email (optional) @@ -353,11 +354,10 @@ class AuthenticationAPI: 200: {"valid": true} rate-limit: 4 auth: no - ''' + """ return self.s.post( "{}/v2/login/password/reset/verify".format(self.root_url), data={ 'token': token, } ) - diff --git a/parler/authentication_v2.py b/parler/authentication_v2.py index 08b9ee3..b79d362 100644 --- a/parler/authentication_v2.py +++ b/parler/authentication_v2.py @@ -48,4 +48,4 @@ class V2AuthenticationAPI: return self.s.post("{}/v2/login/sms/otp/submit".format(self.root_url), *args, **kwargs) def submit_totp(self, *args, **kwargs): - return self.s.post("{}/v2/login/totp/submit".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.post("{}/v2/login/totp/submit".format(self.root_url), *args, **kwargs) diff --git a/parler/campaign_management.py b/parler/campaign_management.py index 8bb9844..cdc76e9 100644 --- a/parler/campaign_management.py +++ b/parler/campaign_management.py @@ -1,4 +1,3 @@ - class CampaignManagementAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url @@ -23,4 +22,5 @@ class CampaignManagementAPI: return self.s.post("{}/v3/promotionNetwork/campaign".format(self.root_url), *args, *kwargs) def submit_campaign(self, identifier, *args, **kwargs): - return self.s.post("{}/v3/promotionNetwork/campaign/{}/submit".format(self.root_url, identifier), *args, *kwargs) \ No newline at end of file + return self.s.post("{}/v3/promotionNetwork/campaign/{}/submit".format(self.root_url, identifier), *args, + *kwargs) diff --git a/parler/campaign_promoter_management.py b/parler/campaign_promoter_management.py index ed715f3..bb7532a 100644 --- a/parler/campaign_promoter_management.py +++ b/parler/campaign_promoter_management.py @@ -1,21 +1,26 @@ - class CampaignManagementPromoterAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url self.s = session def add_promoter_for_request(self, identifier, *args, **kwargs): - return self.s.post("{}/v3/promotionNetwork/campaign/{}/promoter".format(self.root_url, identifier), *args, **kwargs) + return self.s.post("{}/v3/promotionNetwork/campaign/{}/promoter".format(self.root_url, identifier), *args, + **kwargs) def get_details_for_promoter(self, campaignId, promoterId, *args, **kwargs): - return self.s.get("{}/v3/promotionNetwork/campaign/{}/promoter/{}".format(self.root_url, campaignId, promoterId), *args, **kwargs) + return self.s.get( + "{}/v3/promotionNetwork/campaign/{}/promoter/{}".format(self.root_url, campaignId, promoterId), *args, + **kwargs) def get_current_list_of_promoters_for_campaign(self, campaignId, *args, **kwargs): - return self.s.get("{}/v3/promotionNetwork/campaign/{}/promoters".format(self.root_url, campaignId), *args, **kwargs) + return self.s.get("{}/v3/promotionNetwork/campaign/{}/promoters".format(self.root_url, campaignId), *args, + **kwargs) def get_list_of_possible_promoters_for_campaign(self, campaignId, *args, **kwargs): - return self.s.get("{}/v3/promotionNetwork/campaign/{}/promoters/new".format(self.root_url, campaignId), *args, **kwargs) + return self.s.get("{}/v3/promotionNetwork/campaign/{}/promoters/new".format(self.root_url, campaignId), *args, + **kwargs) def remove_promoter_from_campaign(self, campaignId, promoterId, *args, **kwargs): - return self.s.delete("{}/v3/promotionNetwork/campaign/{}/promoter/{}".format(self.root_url, campaignId, promoterId), *args, **kwargs) - + return self.s.delete( + "{}/v3/promotionNetwork/campaign/{}/promoter/{}".format(self.root_url, campaignId, promoterId), *args, + **kwargs) diff --git a/parler/comments.py b/parler/comments.py index 9294683..b508e66 100644 --- a/parler/comments.py +++ b/parler/comments.py @@ -1,4 +1,3 @@ - class CommentsAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url @@ -10,7 +9,7 @@ class CommentsAPI: # id def delete_comment(self, _id, *args, **kwargs): - return self.s.delete("{}/v1/comment".format(self.root_url), params={'id': _id} *args, **kwargs) + return self.s.delete("{}/v1/comment".format(self.root_url), params={'id': _id} * args, **kwargs) # id, up def downvote_comment(self, *args, **kwargs): diff --git a/parler/contacts_uploader.py b/parler/contacts_uploader.py index 7548559..a8a6bd2 100644 --- a/parler/contacts_uploader.py +++ b/parler/contacts_uploader.py @@ -1,4 +1,3 @@ - class ContactsUploaderAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url @@ -14,5 +13,6 @@ class ContactsUploaderAPI: }] } ''' + def start_uploading(self, *args, **kwargs): - return self.s.post("{}/v2/contacts".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.post("{}/v2/contacts".format(self.root_url), *args, **kwargs) diff --git a/parler/content_moderation.py b/parler/content_moderation.py index 30d337d..0aa4256 100644 --- a/parler/content_moderation.py +++ b/parler/content_moderation.py @@ -4,4 +4,4 @@ class ContentModerationAPI: self.s = session def get_reported_content(self, *args, **kwargs): - return self.s.get("{}/v2/contentModeration/reports".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.get("{}/v2/contentModeration/reports".format(self.root_url), *args, **kwargs) diff --git a/parler/conversion.py b/parler/conversion.py index 079fe58..99efe03 100644 --- a/parler/conversion.py +++ b/parler/conversion.py @@ -1,11 +1,10 @@ - class ConversionAPI: def __init__(self, session, root_url="http://api.parler.com"): self.root_url = root_url self.s = session def convert(self, _type, uuid): - ''' + """ GET /v3/idConversion/{type}/{uuid} params: type: one of (user, post, comment, image, video, link) @@ -14,13 +13,13 @@ class ConversionAPI: hidden sequential ID, as protobuf varint rate-limit: none auth: yes - ''' + """ return self.s.get( "{}/v3/idConversion/{}/{}".format(self.root_url, _type, uuid) ) def reverse_convert(self, _type, _id): - ''' + """ GET /v3/uuidConversion/{type}/{id} (*) this is enumerable params: @@ -30,7 +29,7 @@ class ConversionAPI: public-facing UUID of the entity rate-limit: none auth: yes - ''' + """ return self.s.get( "{}/v3/uuidConversion/{}/{}".format(self.root_url, _type, _id) - ) \ No newline at end of file + ) diff --git a/parler/flags.py b/parler/flags.py index 6537456..ddc719c 100644 --- a/parler/flags.py +++ b/parler/flags.py @@ -6,4 +6,3 @@ class FlagsAPI: def get_flags_for(self, userUuid, *args, **kwargs): # i have no idea what this is return self.s.get("{}/v3/flags/user/uuid/{}".format(self.root_url, userUuid), *args, **kwargs) - \ No newline at end of file diff --git a/parler/moderation.py b/parler/moderation.py index af9a370..91eeba4 100644 --- a/parler/moderation.py +++ b/parler/moderation.py @@ -51,7 +51,7 @@ class ModerationAPI: # organization, startkey def get_pending_comments(self, *args, **kwargs): return self.s.get("{}/v1/moderation/pending".format(self.root_url), *args, **kwargs) - + # organization, startkey def get_spam_comments(self, *args, **kwargs): return self.s.get("{}/v1/moderation/spam".format(self.root_url), *args, **kwargs) @@ -89,9 +89,9 @@ class ModerationAPI: def post_iaa_report(self, *args, **kwargs): return self.s.post("{}/v2/iaa/moderation/report".format(self.root_url), *args, **kwargs) - # words, action, organization + # words, action, organization def delete_word_filter(self, *args, **kwargs): return self.s.post("{}/v1/moderation/filter/word/delete".format(self.root_url), *args, **kwargs) def set_nsfw_enabled(self, val, *args, **kwargs): - return self.s.post("{}/v2/profile/toggle/noSensitive/{}".format(self.root_url, val), *args, **kwargs) \ No newline at end of file + return self.s.post("{}/v2/profile/toggle/noSensitive/{}".format(self.root_url, val), *args, **kwargs) diff --git a/parler/parler.py b/parler/parler.py index cf2c07d..5187639 100644 --- a/parler/parler.py +++ b/parler/parler.py @@ -33,12 +33,14 @@ from parler.wallet_general import WalletGeneralAPI from parler.wallet import WalletAPI import requests + class Parler: ''' the api is accessible at api.parler.com and par.pw. staging api = api.speak-free.com mst and jst cookie are same as on web ''' + def __init__(self, mst=None, jst=None, root_url="https://api.parler.com"): session = requests.Session() session.headers['User-Agent'] = 'Parler%20Staging/545 CFNetwork/978.0.7 Darwin 18.7.0' diff --git a/parler/parler_data.py b/parler/parler_data.py index dcd2fd5..9170c78 100644 --- a/parler/parler_data.py +++ b/parler/parler_data.py @@ -4,4 +4,4 @@ class ParlerDataAPI: self.s = session def upload_data(self, *args, **kwargs): - return self.s.post("{}/v2/upload/image".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.post("{}/v2/upload/image".format(self.root_url), *args, **kwargs) diff --git a/parler/posts.py b/parler/posts.py index 938b29e..a0849ae 100644 --- a/parler/posts.py +++ b/parler/posts.py @@ -15,7 +15,6 @@ class PostsAPI: def get_posts_using_urls(self, *args, **kwargs): return self.s.post("{}/v1/wordpress/posts".format(self.root_url), *args, **kwargs) - def get_post_impressions_over_time(self, post, *args, **kwargs): return self.s.get("{}/v1/post/{}/impressions/time".format(self.root_url, post), *args, **kwargs) diff --git a/parler/promoter_campaign_management.py b/parler/promoter_campaign_management.py index 478116e..55f7618 100644 --- a/parler/promoter_campaign_management.py +++ b/parler/promoter_campaign_management.py @@ -4,13 +4,15 @@ class PromoterCampaignManagementAPI: self.s = session def approve_promoter_campaign(self, campaign, *args, **kwargs): - return self.s.post("{}/v3/promotionNetwork/promoter/campaign/{}".format(self.root_url, campaign), *args, **kwargs) + return self.s.post("{}/v3/promotionNetwork/promoter/campaign/{}".format(self.root_url, campaign), *args, + **kwargs) def get_current_status_of_promotion(self, *args, **kwargs): return self.s.get("{}/v3/promotionNetwork/promoter".format(self.root_url), *args, **kwargs) def get_details_for_campaign(self, campaign, *args, **kwargs): - return self.s.get("{}/v3/promotionNetwork/promoter/campaign/{}".format(self.root_url, campaign), *args, **kwargs) + return self.s.get("{}/v3/promotionNetwork/promoter/campaign/{}".format(self.root_url, campaign), *args, + **kwargs) def get_list_of_campaigns(self, *args, **kwargs): return self.s.get("{}/v3/promotionNetwork/promoter/campaigns".format(self.root_url), *args, **kwargs) @@ -22,4 +24,4 @@ class PromoterCampaignManagementAPI: return self.s.post("{}/v3/promotionNetwork/promoter/cpm".format(self.root_url), *args, **kwargs) def set_promoter_guidelines(self, *args, **kwargs): - return self.s.post("{}/v3/promotionNetwork/promoter/guidelines".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.post("{}/v3/promotionNetwork/promoter/guidelines".format(self.root_url), *args, **kwargs) diff --git a/parler/user.py b/parler/user.py index 5246094..e346c5f 100644 --- a/parler/user.py +++ b/parler/user.py @@ -84,4 +84,4 @@ class UserAPI: # id, userId, username def unmute_user(self, *args, **kwargs): - return self.s.delete("{}/v1/user/mute".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.delete("{}/v1/user/mute".format(self.root_url), *args, **kwargs) diff --git a/parler/wallet.py b/parler/wallet.py index 57bb705..533c721 100644 --- a/parler/wallet.py +++ b/parler/wallet.py @@ -4,4 +4,4 @@ class WalletAPI: self.s = session def get_billing_information(self, *args, **kwargs): - return self.s.get("{}/v3/billing".format(self.root_url), *args, **kwargs) \ No newline at end of file + return self.s.get("{}/v3/billing".format(self.root_url), *args, **kwargs) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1cbf91c --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name="parler-api", + version="1.0", + description="Reverse engineered Parler API", + author="d0nk", + author_email="", + packages=["parler_api"], + install_requires=[ + "requests", + ] +)