Initial commit

This commit is contained in:
d0nk 2020-12-08 01:55:02 +01:00
commit 403583717b
37 changed files with 1304 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
parler/__pycache__/

15
LICENSE Normal file
View File

@ -0,0 +1,15 @@
I SOLEMNLY SWEAR THAT I AM UP TO NO GOOD PUBLIC LICENSE
Version 1, December 2020
Copyright (C) 2020 d0nk
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
I SOLEMNLY SWEAR THAT I AM UP TO NO GOOD PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You solemnly swear that you are up to no good.
1. You just DO WHAT THE FUCK YOU WANT TO.

33
README.md Normal file
View File

@ -0,0 +1,33 @@
Parler's unofficial API with all endpoints present in their iOS app as of 08/12/2020.
For the most part undocumented, but the error responses are already quite descriptive.
Any endpoint starting with `/v3/` is protobuf-based. The .proto files can be found elsewhere.
Use it to solve fun mysteries such as:
- Is my dad on Parler?
- Who was on Parler before it first started gaining popularity when Candice Owens tweeted about in December 2018?
- Which users have administration and moderation rights? (hint: `(interactions >> 5) & 1` = moderator, `(interactions >> 6) & 1` = admin)
- What exactly is an "integration partner", and which media entities currently are they?
- If Parler is really yet to come up with a business model for how to make money, then what exactly is a Campaign Promoter Management Network?
- How does the payment integration that lets users be financially rewarded for their posts play into that?
- Whenever John Matze says "influencer marketing" should I really be hearing, "we've created a pavlovian conditioning machine that rewards already gullible people for organically spreading disinformation campaigns"? (most likely)
- Is Parler really the world's most secure social network? (no)
I rushed this just so I didn't have to stare at ghidra anymore. There might be bugs/typos. Patches welcome.
### Usage ###
```python
from parler import Parler
parler = Parler('mst_cookie', 'jst_cookie') # You can also just do the login flow.
parler.user_api.get_profile_for_user(params={'id': 'a078092e6e2df507bfd12db4710202ab'})
```
### Disclaimer ###
Digital Millennium Copyright Act (DMCA) USC § 1201 (f) states:
A person who has lawfully obtained the right to use a copy of a computer program may circumvent a technological measure that effectively controls access to a particular portion of that program for the sole purpose of identifying and analyzing those elements of the program that are necessary to achieve interoperability of an independently created computer program with other programs, and that have not previously been readily available to the person engaging in the circumvention, to the extent any such acts of identification and analysis do not constitute infringement under this title.

1
parler/__init__.py Normal file
View File

@ -0,0 +1 @@
from parler.parler import Parler

363
parler/authentication.py Normal file
View File

@ -0,0 +1,363 @@
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
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),
data={
'password': password,
'passwordConfirm': passwordConfirm
})
def authenticate(self, email, password, captchaToken, captchaValue, deviceId=None):
'''
POST /v1/login
params:
email
password
captchaToken
captchaValue
deviceId
deviceType - optional
notificationId - optional
response:
rate-limit: 5
auth: no
'''
if deviceId is None:
deviceId = str(uuid.uuid4())
return self.s.post(
"{}/v1/login".format(self.root_url),
data={
'email': email,
'password': password,
'captchaToken': captchaToken,
'captchaValue': captchaValue,
'deviceId': deviceId
})
def begin_registration_otp(self):
'''
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
passwordNew
passwordConfirmation
response:
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),
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:
email
password
passwordConfirm
deviceType
ios
deviceId
notificationId
captchaToken
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),
data={
'email': email,
'password': password,
'passwordConfirm': password,
'captchaToken': captchaToken,
'captchaValue': captchaValue,
'deviceId': deviceId
})
def disable_otp(self, totp):
'''
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),
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
secret - can provide own secet
response:
200: {"message": "OK"}
rate-limit: 5
auth: yes
'''
return self.s.post(
"{}/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:
authenticationToken
auth: yes
'''
return self.s.post(
"{}/v1/logout".format(self.root_url),
data={
'authenticationToken': authenticationToken
}
)
def request_password_reset(self, email):
'''
POST /v1/login/resetReqeust
params:
email
response:
200: {"message": "OK"}
rate-limit: 3
auth: no
'''
return self.s.post(
"{}/v1/login/resetRequest".format(self.root_url),
data={
'email': email
}
)
def request_signin_link_for_email(self, email, captchaToken, captchaValue, deviceId):
'''
POST /v1/signin/request
removed
params:
email
deviceId
captchaToken
captchaValue
response:
404: {"message": "No resource for URL", "fieldvalidationsMap": []}}
'''
if deviceId is None:
deviceId = str(uuid.uuid4())
return self.s.post(
"{}/v1/signin/request".format(self.root_url),
data={
'email': email,
'captchaToken': captchaToken,
'captchaValue': captchaValue,
'deviceId': deviceId
}
)
def reset_user_password(self, password, passwordConfirm, resetToken):
'''
POST /v2/login/password/reset/submit
params:
password
passwordConfirm
resetToken: 16 char uppercase alphanum
response:
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 = {
'password': password,
'passwordConfirm': passwordConfirm,
'resetToken': resetToken
}
)
def send_email_reminder(self, username):
'''
POST /v1/login/reminder
sends email reminder for username
params:
username
response:
200: {"message": "success"}
rate-limit: 15
auth: no
'''
return self.s.post(
"{}/v1/login/reminder".format(self.root_url),
data={
'username': username
}
)
def send_notification_token(self, notificationId):
'''
POST /v1/notification
Mobile push notification token
params:
notificationId
response:
200: {"message": "success"}
rate-limit: 5
auth: yes
'''
return self.s.post(
"{}/v1/notification".format(self.root_url),
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:
email
code
deviceType
deviceId
ios
notificationId
response:
404: { "message": "No resource for URL", "fieldvalidationsMap": [] }
'''
if deviceId is None:
deviceId = str(uuid.uuid4())
return self.s.post(
"{}/v1/signin".format(self.root_url),
data={
'deviceId': deviceId,
'email': email,
'code': code
}
)
def sign_up_email(self, email, name, deviceId=None):
'''
POST /v1/signup
removed
params:
email
name
deviceId
response:
404: { "message": "No resource for URL", "fieldvalidationsMap": [] }
'''
if deviceId is None:
deviceId = str(uuid.uuid4())
return self.s.post(
"{}/v1/signup".format(self.root_url),
data={
'deviceId': deviceId,
'email': email,
'name': name
}
)
def verify_reset_code(self, token):
'''
POST /v2/login/password/reset/verify
params:
email (optional)
token 16 char uppercase alphanum
response:
400: {"message": "The provided password reset code is invalid", "fieldvalidationsMap": []}
200: {"valid": true}
rate-limit: 4
auth: no
'''
return self.s.post(
"{}/v2/login/password/reset/verify".format(self.root_url),
data={
'token': token,
}
)

View File

@ -0,0 +1,51 @@
class V2AuthenticationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def add_phone(self, *args, **kwargs):
return self.s.post("{}/v2/login/sms/add".format(self.root_url), *args, **kwargs)
# email, phone, password, passwordConfirm, deviceId, notificationId (opt), deviceType (opt), ios (opt)
def apply_for_account(self, *args, **kwargs):
return self.s.post("{}/v2/login/apply".format(self.root_url), *args, **kwargs)
# phone, password
def change_add_phone(self, *args, **kwargs):
return self.s.post("{}/v2/login/sms/change".format(self.root_url), *args, **kwargs)
def get_a_captcha(self, *args, **kwargs):
return self.s.post("{}/v2/login/captcha/new".format(self.root_url), *args, **kwargs)
def get_captcha_for_email(self, *args, **kwargs):
return self.s.post("{}/v2/login/password/reset/captcha".format(self.root_url), *args, **kwargs)
# identifier, password, deviceId, notificationId (opt), deviceType (opt), ios (opt)
def login_phone_or_email(self, *args, **kwargs):
return self.s.post("{}/v2/login/new".format(self.root_url), *args, **kwargs)
def logout(self, *args, **kwargs):
return self.s.post("{}/v2/logout".format(self.root_url), *args, **kwargs)
def re_request_smsopt(self, *args, **kwargs):
return self.s.post("{}/v2/login/sms/opt/resend".format(self.root_url), *args, **kwargs)
# identifier, captchaKey
def reset_password_for_email(self, *args, **kwargs):
return self.s.post("{}/v2/login/password/reset/new".format(self.root_url), *args, **kwargs)
# identifier, doesn't work to skip phone auth
def skip_step(self, *args, **kwargs):
return self.s.post("{}/v2/login/skip".format(self.root_url), *args, **kwargs)
def submit_captcha(self, *args, **kwargs):
return self.s.post("{}/v2/login/captcha/submit".format(self.root_url), *args, **kwargs)
def submit_phone_change(self, *args, **kwargs):
return self.s.post("{}/v2/login/sms/change/submit".format(self.root_url), *args, **kwargs)
def submit_smsopt(self, *args, **kwargs):
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)

View File

@ -0,0 +1,26 @@
class CampaignManagementAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def delete_campaign(self, identifier, *args, **kwargs):
return self.s.delete("{}/v3/promotionNetwork/campaign/{}".format(self.root_url, identifier), *args, *kwargs)
def get_current_users_stats_for_promotion(self, *args, **kwargs):
return self.s.get("{}/v3/promotionNetwork/stats".format(self.root_url), *args, *kwargs)
def get_details_for_campaign(self, identifier, *args, **kwargs):
return self.s.get("{}/v3/promotionNetwork/campaign/{}".format(self.root_url, identifier), *args, *kwargs)
def get_list_of_campaigns(self, *args, **kwargs):
return self.s.get("{}/v3/promotionNetwork/campaigns".format(self.root_url), *args, *kwargs)
def set_details_for_campaign(self, identifier, *args, **kwargs):
return self.s.post("{}/v3/promotionNetwork/campaign/{}".format(self.root_url, identifier), *args, *kwargs)
def start_creating_new_campaign(self, *args, **kwargs):
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)

View File

@ -0,0 +1,21 @@
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)
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)
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)
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)
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)

33
parler/comments.py Normal file
View File

@ -0,0 +1,33 @@
class CommentsAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# parent, body, links, sensitive
def create_comment(self, *args, **kwargs):
return self.s.post("{}/v1/comment/async".format(self.root_url), *args, **kwargs)
# id
def delete_comment(self, _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):
return self.s.post("{}/v1/comment/vote".format(self.root_url), *args, **kwargs)
# sortBy, reverse, startKey, id
def get_comments(self, *args, **kwargs):
return self.s.get("{}/v1/comment".format(self.root_url), *args, **kwargs)
# startKey, conversation, id, sortBy
def get_comments_by_conversation(self, *args, **kwargs):
return self.s.get("{}/v1/comment".format(self.root_url), *args, **kwargs)
# id
def remove_comment_votes(self, *args, **kwargs):
return self.s.delete("{}/v1/comment/vote".format(self.root_url), *args, **kwargs)
# id, up
def upvote_comment(self, *args, **kwargs):
return self.s.post("{}/v1/comment/vote".format(self.root_url), *args, **kwargs)

View File

@ -0,0 +1,18 @@
class ContactsUploaderAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
'''
{
"contactsList": [{
"firstName": "",
"email": "",
"lastName": "",
"phone": ""
}]
}
'''
def start_uploading(self, *args, **kwargs):
return self.s.post("{}/v2/contacts".format(self.root_url), *args, **kwargs)

View File

@ -0,0 +1,7 @@
class ContentModerationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def get_reported_content(self, *args, **kwargs):
return self.s.get("{}/v2/contentModeration/reports".format(self.root_url), *args, **kwargs)

36
parler/conversion.py Normal file
View File

@ -0,0 +1,36 @@
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)
uuid: public-facing UUID of the entity
response:
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:
type: one of (user, post, comment, image, video, link)
id: hidden, sequential ID
response:
public-facing UUID of the entity
rate-limit: none
auth: yes
'''
return self.s.get(
"{}/v3/uuidConversion/{}/{}".format(self.root_url, _type, _id)
)

25
parler/deposit.py Normal file
View File

@ -0,0 +1,25 @@
class DepositAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def execute_deposit(self, identifier, *args, **kwargs):
return self.s.put("{}/v3/wallet/deposit/{}".format(self.root_url, identifier), *args, **kwargs)
def load_deposits_list(self, *args, **kwargs):
return self.s.get("{}/v3/wallet/deposits".format(self.root_url), *args, **kwargs)
def load_details_for_deposit(self, identifier, *args, **kwargs):
return self.s.get("{}/v3/wallet/deposit/{}".format(self.root_url, identifier), *args, **kwargs)
def make_cancelled_deposit(self, identifier, *args, **kwargs):
return self.s.delete("{}/v3/wallet/deposit/{}".format(self.root_url, identifier), *args, **kwargs)
def send_iap_receipt(self, *args, **kwargs):
return self.s.post("{}/v3/wallet/iap/ios".format(self.root_url), *args, **kwargs)
def set_amount_for_deposit(self, identifier, *args, **kwargs):
return self.s.post("{}/v3/wallet/deposit/{}".format(self.root_url, identifier), *args, **kwargs)
def start_deposit_with_data(self, *args, **kwargs):
return self.s.post("{}/v3/wallet/deposit".format(self.root_url), *args, **kwargs)

27
parler/discover.py Normal file
View File

@ -0,0 +1,27 @@
class DiscoverAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def add_post_to_discover_page(self, postId, *args, **kwargs):
return self.s.post("{}/v1/discover/posts/{}/add".format(self.root_url, postId), *args, **kwargs)
# limit, startKey
def get_discover_hashtags(self, *args, **kwargs):
# app has this bug too :)
return self.s.get("{}/v1/discover/hashtags".format(self.root_url), *args, **kwargs)
# limit, startKey
def get_discover_news(self, *args, **kwargs):
return self.s.get("{}/v1/discover/news".format(self.root_url), *args, **kwargs)
# limit, startKey
def get_discover_posts(self, *args, **kwargs):
return self.s.get("{}/v1/discover/posts".format(self.root_url), *args, **kwargs)
# limit, startKey
def get_discover_users(self, *args, **kwargs):
return self.s.get("{}/v1/discover/users".format(self.root_url), *args, **kwargs)
def remove_post_from_discover_page(self, postId, *args, **kwargs):
return self.s.delete("{}/v1/discover/posts/{}/remove".format(self.root_url, postId), *args, **kwargs)

39
parler/feed.py Normal file
View File

@ -0,0 +1,39 @@
class FeedAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# startKey, followingOnly, hideEchoes, onlySubscribed
def get_feed(self, *args, **kwargs):
return self.s.get("{}/v1/feed".format(self.root_url), *args, **kwargs)
# id, startKey
def get_media_content_for_user(self, *args, **kwargs):
return self.s.get("{}/v1/post/creator/media".format(self.root_url), *args, **kwargs)
# tag, startKey
def get_posts_for_hashtag(self, *args, **kwargs):
return self.s.get("{}/v1/post/hashtag".format(self.root_url), *args, **kwargs)
# id, startKey
def get_users_comments(self, *args, **kwargs):
return self.s.get("{}/v1/comment/creator".format(self.root_url), *args, **kwargs)
# id, startKey
def get_users_feed(self, *args, **kwargs):
return self.s.get("{}/v1/post/creator".format(self.root_url), *args, **kwargs)
# id, startKey
def get_users_likes(self, *args, **kwargs):
return self.s.get("{}/v1/post/creator/liked".format(self.root_url), *args, **kwargs)
# id, startKey
def get_users_news(self, *args, **kwargs):
return self.s.get("{}/v1/post/creator/media".format(self.root_url), *args, **kwargs)
def integration_partner_feed(self, *args, **kwargs):
return self.s.get("{}/v1/post/integration".format(self.root_url), *args, **kwargs)
# startkey, limit
def news_feed(self, *args, **kwargs):
return self.s.get("{}/v1/news".format(self.root_url), *args, **kwargs)

7
parler/feedback.py Normal file
View File

@ -0,0 +1,7 @@
class FeedbackAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def submit_feedback(self, *args, **kwargs):
return self.s.post("{}/v3/feedback".format(self.root_url), *args, **kwargs)

9
parler/flags.py Normal file
View File

@ -0,0 +1,9 @@
class FlagsAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
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)

35
parler/follow.py Normal file
View File

@ -0,0 +1,35 @@
class FollowAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# id
def approve_follower_request(self, *args, **kwargs):
return self.s.post("{}/v1/follow/followers/pending/approve".format(self.root_url), *args, **kwargs)
# id
def deny_follower_request(self, *args, **kwargs):
return self.s.post("{}/v1/follow/followers/pending/deny".format(self.root_url), *args, **kwargs)
# id
def follow_user(self, *args, **kwargs):
return self.s.post("{}/v1/follow".format(self.root_url), *args, **kwargs)
# startkey
def get_pending_follower_requests(self, *args, **kwargs):
return self.s.get("{}/v1/follow/followers/pending".format(self.root_url), *args, **kwargs)
def pending_count(self, *args, **kwargs):
return self.s.get("{}/v1/follow/followers/pending/count".format(self.root_url), *args, **kwargs)
# id, enabled = 1
def subscribe_to_user(self, *args, **kwargs):
return self.s.post("{}/v1/follow/following/subscribed".format(self.root_url), *args, **kwargs)
# id
def unfollow_user(self, *args, **kwargs):
return self.s.delete("{}/v1/follow".format(self.root_url), *args, **kwargs)
# id. enabled = 0
def unsubscribe_from_user(self, *args, **kwargs):
return self.s.post("{}/v1/follow/following/subscribed".format(self.root_url), *args, **kwargs)

10
parler/image.py Normal file
View File

@ -0,0 +1,10 @@
class ImageAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def set_profile_photo(self, *args, **kwargs):
return self.s.post("{}/v1/profile/photo".format(self.root_url), *args, **kwargs)
def set_profile_cover(self, *args, **kwargs):
return self.s.post("{}/v1/profile/cover-photo".format(self.root_url), *args, **kwargs)

16
parler/link.py Normal file
View File

@ -0,0 +1,16 @@
class LinkAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# link
def post_links_with_url(self, *args, **kwargs):
return self.s.post("{}/v1/link".format(self.root_url), *args, **kwargs)
# link
def post_link_with_giphy(self, *args, **kwargs):
return self.s.post("{}/v1/link".format(self.root_url), *args, **kwargs)
# longURL, metadata, link, links
def post_url_link(self, *args, **kwargs):
return self.s.post("{}/v1/link".format(self.root_url), *args, **kwargs)

48
parler/messaging.py Normal file
View File

@ -0,0 +1,48 @@
class MessagingAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def accept_conversation(self, _id, *args, **kwargs):
return self.s.post("{}/v1/messaging/conversations/{}/accept".format(self.root_url, _id), *args, **kwargs)
def find_or_create_conversation_with_user(self, user, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/user/{}".format(self.root_url, user), *args, **kwargs)
# limit
def get_conversation_requests(self, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/requests".format(self.root_url), *args, **kwargs)
# limit, startkey
def get_conversations(self, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations".format(self.root_url), *args, **kwargs)
def get_conversation(self, _id, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/{}".format(self.root_url, _id), *args, **kwargs)
# limit, startkey
def get_messages_for_conversation(self, _id, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/{}/messages".format(self.root_url, _id), *args, **kwargs)
def get_messaging_counts(self, *args, **kwargs):
return self.s.get("{}/v1/messaging/counts".format(self.root_url), *args, **kwargs)
# limit, startkey
def get_unread_conversations(self, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/unread".format(self.root_url), *args, **kwargs)
def hide_conversation(self, _id, *args, **kwargs):
return self.s.post("{}/v1/messaging/conversations/{}/hide".format(self.root_url, _id), *args, **kwargs)
def mark_all_message_requests_read(self, *args, **kwargs):
return self.s.post("{}/v1/messaging/conversations/requests".format(self.root_url), *args, **kwargs)
def mark_conversation_as_read(self, _id, *args, **kwargs):
return self.s.post("{}/v1/messaging/conversations/{}/read".format(self.root_url, _id), *args, **kwargs)
def post_message(self, _id, *args, **kwargs):
return self.s.post("{}/v1/messaging/conversations/{}/messages".format(self.root_url, _id), *args, **kwargs)
# search, startkey
def search_for_mutual_follow_users(self, *args, **kwargs):
return self.s.get("{}/v1/messaging/conversations/user/".format(self.root_url), *args, **kwargs)

97
parler/moderation.py Normal file
View File

@ -0,0 +1,97 @@
class ModerationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# words, action, organization
def add_word_filter(self, *args, **kwargs):
# the free speech social network xd
return self.s.post("{}/v1/moderation/filter/word".format(self.root_url), *args, **kwargs)
# organization, id
def approve_all_pending_comments_for_user_id(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/approve/user".format(self.root_url), *args, **kwargs)
# organization, comments
def approve_comments(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/approve".format(self.root_url), *args, **kwargs)
def block_link(self, domain, *args, **kwargs):
return self.s.post("{}/v2/iaa/moderation/domain/{}".format(self.root_url, domain), *args, **kwargs)
def delete_iaa_report(self, report, *args, **kwargs):
return self.s.delete("{}/v2/iaa/moderation/report/{}".format(self.root_url, report), *args, **kwargs)
# organization, id
def deny_all_comments_for_user_id(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/deny/user".format(self.root_url), *args, **kwargs)
# organization, comments
def deny_comments(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/deny".format(self.root_url), *args, **kwargs)
# organization, startkey
def get_approved_comments(self, *args, **kwargs):
return self.s.get("{}/v1/moderation/approved".format(self.root_url), *args, **kwargs)
# organization, startkey
def get_denied_comments(self, *args, **kwargs):
return self.s.get("{}/v1/moderation/denied".format(self.root_url), *args, **kwargs)
def get_moderation_counts(self, *args, **kwargs):
return self.s.get("{}/v1/moderation/count".format(self.root_url), *args, **kwargs)
def get_iaa_report(self, *args, **kwargs):
return self.s.get("{}/v2/iaa/moderation/report".format(self.root_url), *args, **kwargs)
# organization, startkey
def get_muted_comments(self, *args, **kwargs):
return self.s.get("{}/v1/moderation/muted".format(self.root_url), *args, **kwargs)
# 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)
# action, startkey
def get_word_filters(self, *args, **kwargs):
return self.s.get("{}/v1/moderation/filter/word".format(self.root_url), *args, **kwargs)
# organization, id
def mark_all_comments_for_user_id_as_spam(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/spam/user".format(self.root_url), *args, **kwargs)
def mark_comment_sensitive(self, comment, *args, **kwargs):
return self.s.post("{}/v2/iaa/sensitive/comment/{}".format(self.root_url, comment), *args, **kwargs)
# organization, comments
def mark_comments_as_spam(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/spam".format(self.root_url), *args, **kwargs)
def mark_post_sensitive(self, post, *args, **kwargs):
return self.s.post("{}/v2/iaa/sensitive/post/{}".format(self.root_url, post), *args, **kwargs)
def mark_user_sensitive(self, user, *args, **kwargs):
return self.s.post("{}/v2/iaa/sensitive/user/{}".format(self.root_url, user), *args, **kwargs)
# organization, id
def mute_all_comments_for_user_id(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/mute/user".format(self.root_url), *args, **kwargs)
# comments, organization
def mute_comments(self, *args, **kwargs):
return self.s.post("{}/v1/moderation/mute".format(self.root_url), *args, **kwargs)
# displayable, value, notes
def post_iaa_report(self, *args, **kwargs):
return self.s.post("{}/v2/iaa/moderation/report".format(self.root_url), *args, **kwargs)
# 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)

18
parler/notification.py Normal file
View File

@ -0,0 +1,18 @@
class NotificationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def delete_all_notifications(self, *args, **kwargs):
return self.s.delete("{}/v1/notification/all".format(self.root_url), *args, **kwargs)
# id
def delete_notification(self, *args, **kwargs):
return self.s.delete("{}/v1/notification".format(self.root_url), *args, **kwargs)
def get_badge_count(self, *args, **kwargs):
return self.s.get("{}/v1/notification/count".format(self.root_url), *args, **kwargs)
# startkey, action
def get_notifications(self, *args, **kwargs):
return self.s.get("{}/v1/notification".format(self.root_url), *args, **kwargs)

80
parler/parler.py Normal file
View File

@ -0,0 +1,80 @@
from parler.authentication import AuthenticationAPI
from parler.authentication_v2 import V2AuthenticationAPI
from parler.campaign_management import CampaignManagementAPI
from parler.campaign_promoter_management import CampaignManagementPromoterAPI
from parler.comments import CommentsAPI
from parler.contacts_uploader import ContactsUploaderAPI
from parler.content_moderation import ContentModerationAPI
from parler.conversion import ConversionAPI
from parler.deposit import DepositAPI
from parler.discover import DiscoverAPI
from parler.feed import FeedAPI
from parler.feedback import FeedbackAPI
from parler.flags import FlagsAPI
from parler.follow import FollowAPI
from parler.image import ImageAPI
from parler.link import LinkAPI
from parler.messaging import MessagingAPI
from parler.moderation import ModerationAPI
from parler.notification import NotificationAPI
from parler.parler_data import ParlerDataAPI
from parler.parler_video import ParlerVideoAPI
from parler.posts import PostsAPI
from parler.search import SearchAPI
from parler.settings import SettingsAPI
from parler.tipping import TippingAPI
from parler.transaction import TransactionAPI
from parler.user_verification import UserVerificationAPI
from parler.user import UserAPI
from parler.violation import ViolationAPI
from parler.wallet_card import WalletCardAPI
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'
if mst is not None:
session.cookies['mst'] = mst
if jst is not None:
session.cookies['jst'] = jst
self.authentication_api = AuthenticationAPI(session, root_url)
self.v2_authentication_api = V2AuthenticationAPI(session, root_url)
self.campaign_management_api = CampaignManagementAPI(session, root_url)
self.campaign_promoter_management_api = CampaignManagementPromoterAPI(session, root_url)
self.comments_api = CommentsAPI(session, root_url)
self.contacts_uploader_api = ContactsUploaderAPI(session, root_url)
self.content_moderation_api = ContentModerationAPI(session, root_url)
self.conversion_api = ConversionAPI(session, root_url)
self.deposit_api = DepositAPI(session, root_url)
self.discover_api = DiscoverAPI(session, root_url)
self.feed_api = FeedAPI(session, root_url)
self.feedback_api = FeedbackAPI(session, root_url)
self.flags_api = FlagsAPI(session, root_url)
self.follow_api = FollowAPI(session, root_url)
self.image_api = ImageAPI(session, root_url)
self.link_api = LinkAPI(session, root_url)
self.messaging_api = MessagingAPI(session, root_url)
self.moderation_api = ModerationAPI(session, root_url)
self.notification_api = NotificationAPI(session, root_url)
self.parler_data_api = ParlerDataAPI(session, root_url)
self.parler_video_api = ParlerVideoAPI(session, root_url)
self.posts_api = PostsAPI(session, root_url)
self.search_api = SearchAPI(session, root_url)
self.settings_api = SettingsAPI(session, root_url)
self.tipping_api = TippingAPI(session, root_url)
self.transaction_api = TransactionAPI(session, root_url)
self.user_api = UserAPI(session, root_url)
self.user_verification = UserVerificationAPI(session, root_url)
self.violation_api = ViolationAPI(session, root_url)
self.wallet_card_api = WalletCardAPI(session, root_url)
self.wallet_general_api = WalletGeneralAPI(session, root_url)
self.wallet_api = WalletAPI(session, root_url)

7
parler/parler_data.py Normal file
View File

@ -0,0 +1,7 @@
class ParlerDataAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def upload_data(self, *args, **kwargs):
return self.s.post("{}/v2/upload/image".format(self.root_url), *args, **kwargs)

19
parler/parler_video.py Normal file
View File

@ -0,0 +1,19 @@
class ParlerVideoAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def get_current_video_status(self, *args, **kwargs):
return self.s.get("{}/v2/upload/video/status".format(self.root_url), *args, **kwargs)
def get_video_processing_status(self, video, *args, **kwargs):
return self.s.get("{}/v2/upload/video/status/{}".format(self.root_url, video), *args, **kwargs)
def cancel_video_process(self, video, *args, **kwargs):
return self.s.delete("{}/v2/upload/video/cancel/{}".format(self.root_url, video), *args, **kwargs)
def notify_server_of_the_finished_uploaded_video(self, video, *args, **kwargs):
return self.s.post("{}/v2/upload/video/uploaded/{}".format(self.root_url, video), *args, **kwargs)
def request_video_upload_url(self, *args, **kwargs):
return self.s.post("{}/v2/upload/video/new".format(self.root_url), *args, **kwargs)

43
parler/posts.py Normal file
View File

@ -0,0 +1,43 @@
class PostsAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# id
def delete_post(self, *args, **kwargs):
return self.s.delete("{}/v1/post".format(self.root_url), *args, **kwargs)
# id
def dislike_post(self, *args, **kwargs):
return self.s.post("{}/v1/user/dislike".format(self.root_url), *args, **kwargs)
# urls
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)
def get_post_impressions(self, post, *args, **kwargs):
return self.s.get("{}/v1/post/{}/impressions".format(self.root_url, post), *args, **kwargs)
# parent, body, links
def repost_post_id(self, *args, **kwargs):
return self.s.post("{}/v1/post".format(self.root_url), *args, **kwargs)
# id
def unvote_post(self, *args, **kwargs):
return self.s.delete("{}/v1/post/upvote".format(self.root_url), *args, **kwargs)
# id
def upvote_post(self, *args, **kwargs):
return self.s.post("{}/v1/post/upvote".format(self.root_url), *args, **kwargs)
# body, links, sensitive
def create_post(self, *args, **kwargs):
return self.s.post("{}/v1/post/async".format(self.root_url), *args, **kwargs)
# id
def get_post(self, *args, **kwargs):
return self.s.get("{}/v1/post".format(self.root_url), *args, **kwargs)

20
parler/search.py Normal file
View File

@ -0,0 +1,20 @@
class SearchAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# search, startkey, domain (optional)
def search_for_news(self, *args, **kwargs):
return self.s.get("{}/v1/news/search".format(self.root_url), *args, **kwargs)
# tag, startkey
def search_for_posts_with_hashtag(self, *args, **kwargs):
return self.s.get("{}/v1/post/hashtag".format(self.root_url), *args, **kwargs)
# search, startkey
def search_for_users(self, *args, **kwargs):
return self.s.get("{}/v1/users".format(self.root_url), *args, **kwargs)
# search, startkey
def search_hashtags(self, *args, **kwargs):
return self.s.get("{}/v1/hashtag".format(self.root_url), *args, **kwargs)

20
parler/settings.py Normal file
View File

@ -0,0 +1,20 @@
class SettingsAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def get_security_settings(self, *args, **kwargs):
return self.s.get("{}/v1/profile/settings/security".format(self.root_url), *args, **kwargs)
def get_settings(self, *args, **kwargs):
return self.s.get("{}/v1/profile/settings".format(self.root_url), *args, **kwargs)
def get_verification_page_data(self, *args, **kwargs):
return self.s.get("{}/v1/profile/verify/form".format(self.root_url), *args, **kwargs)
def patch_setting(self, *args, **kwargs):
return self.s.post("{}/v1/profile/settings".format(self.root_url), *args, **kwargs)
# badge
def set_primary_badge(self, *args, **kwargs):
return self.s.post("{}/v1/profile/badge/display".format(self.root_url), *args, **kwargs)

7
parler/tipping.py Normal file
View File

@ -0,0 +1,7 @@
class TippingAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def set_tips_with_id(self, *args, **kwargs):
return self.s.post("{}/v3/tip".format(self.root_url), *args, **kwargs)

10
parler/transaction.py Normal file
View File

@ -0,0 +1,10 @@
class TransactionAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def load_details_for_transaction(self, transaction, *args, **kwargs):
return self.s.get("{}/v3/wallet/transaction/{}".format(self.root_url, transaction), *args, **kwargs)
def load_list_of_transactions(self, *args, **kwargs):
return self.s.get("{}/v3/wallet/transactions".format(self.root_url), *args, **kwargs)

87
parler/user.py Normal file
View File

@ -0,0 +1,87 @@
class UserAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
# message
def ban_user(self, user, *args, **kwargs):
return self.s.post("{}/v2/iaa/user/{}/ban".format(self.root_url, user), *args, **kwargs)
# password
def delete_account_with_password(self, *args, **kwargs):
return self.s.post("{}/v2/profile/delete".format(self.root_url), *args, **kwargs)
def get_integration_partners(self, *args, **kwargs):
return self.s.get("{}/v1/users/integrations".format(self.root_url), *args, **kwargs)
def get_rss_partners(self, *args, **kwargs):
return self.s.get("{}/v1/users/rss".format(self.root_url), *args, **kwargs)
def get_user_verification_status(self, *args, **kwargs):
return self.s.get("{}/v1/identity/status".format(self.root_url), *args, **kwargs)
# front, back, selfOpen, selfClosed, barcodeText
def verify_user(self, *args, **kwargs):
return self.s.post("{}/v1/identity/submit".format(self.root_url), *args, **kwargs)
# id, userId
def block_user(self, *args, **kwargs):
return self.s.post("{}/v1/user/block".format(self.root_url), *args, **kwargs)
# accountColor
def change_profile_color_theme(self, *args, **kwargs):
return self.s.patch("{}/v1/profile".format(self.root_url), *args, **kwargs)
# username
def does_username_exist(self, *args, **kwargs):
return self.s.get("{}/v1/user/exists".format(self.root_url), *args, **kwargs)
# limit, startkey
def get_blocked_users(self, *args, **kwargs):
return self.s.get("{}/v1/user/block".format(self.root_url), *args, **kwargs)
# id, startkey
def get_followers_for_user_id(self, *args, **kwargs):
return self.s.get("{}/v1/follow/followers".format(self.root_url), *args, **kwargs)
# id, startkey
def get_following_for_user_id(self, *args, **kwargs):
return self.s.get("{}/v1/follow/following".format(self.root_url), *args, **kwargs)
def get_muted_users(self, *args, **kwargs):
return self.s.get("{}/v1/user/mute".format(self.root_url), *args, **kwargs)
# id
def get_profile_for_user(self, *args, **kwargs):
return self.s.get("{}/v1/profile".format(self.root_url), *args, **kwargs)
# id
def get_profile_for_username(self, *args, **kwargs):
return self.s.get("{}/v1/profile".format(self.root_url), *args, **kwargs)
# startkey
def get_subscribed(self, *args, **kwargs):
return self.s.get("{}/v1/follow/following/subscribed".format(self.root_url), *args, **kwargs)
# id, startkey
def get_unfollowing(self, *args, **kwargs):
return self.s.get("{}/v1/recommend/unfollow".format(self.root_url), *args, **kwargs)
# id, userId, username
def mute_user(self, *args, **kwargs):
return self.s.post("{}/v1/user/mute".format(self.root_url), *args, **kwargs)
def patch_profile(self, *args, **kwargs):
return self.s.patch("{}/v1/profile".format(self.root_url), *args, **kwargs)
# id, reason
def report_object(self, *args, **kwargs):
return self.s.post("{}/v1/user/report".format(self.root_url), *args, **kwargs)
# id, userId, username
def unblock_user(self, *args, **kwargs):
return self.s.delete("{}/v1/user/block".format(self.root_url), *args, **kwargs)
# id, userId, username
def unmute_user(self, *args, **kwargs):
return self.s.delete("{}/v1/user/mute".format(self.root_url), *args, **kwargs)

View File

@ -0,0 +1,16 @@
class UserVerificationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def get_user_verification_list(self, *args, **kwargs):
return self.s.get("{}/v3/userVerifications".format(self.root_url), *args, **kwargs)
def get_user_verification_status_details(self, verification, *args, **kwargs):
return self.s.get("{}/v3/userVerification/{}".format(self.root_url, verification), *args, **kwargs)
def get_user_verification_status(self, *args, **kwargs):
return self.s.get("{}/v3/userVerification".format(self.root_url), *args, **kwargs)
def submit_user_verification_status(self, *args, **kwargs):
return self.s.post("{}/v3/userVerification".format(self.root_url), *args, **kwargs)

17
parler/violation.py Normal file
View File

@ -0,0 +1,17 @@
class ViolationAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def create_report(self, *args, **kwargs):
return self.s.post("{}/v3/contentModeration/report".format(self.root_url), *args, **kwargs)
def get_details_for_report_with_id(self, report, *args, **kwargs):
return self.s.get("{}/v3/contentModeration/report/{}".format(self.root_url, report), *args, **kwargs)
# startPage, limit
def get_list_of_reports(self, *args, **kwargs):
return self.s.get("{}/v3/contentModeration/reports".format(self.root_url), *args, **kwargs)
def get_stats(self, *args, **kwargs):
return self.s.get("{}/v3/contentModeration/stats".format(self.root_url), *args, **kwargs)

7
parler/wallet.py Normal file
View File

@ -0,0 +1,7 @@
class WalletAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def get_billing_information(self, *args, **kwargs):
return self.s.get("{}/v3/billing".format(self.root_url), *args, **kwargs)

16
parler/wallet_card.py Normal file
View File

@ -0,0 +1,16 @@
class WalletCardAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def create_payment_method_from_data(self, *args, **kwargs):
return self.s.post("{}/v3/wallet/card/new".format(self.root_url), *args, **kwargs)
def delete_payment_method(self, method, *args, **kwargs):
return self.s.delete("{}/v3/wallet/card/{}".format(self.root_url, method), *args, **kwargs)
def load_details_for_payment_method(self, method, *args, **kwargs):
return self.s.get("{}/v3/wallet/card/{}".format(self.root_url, method), *args, **kwargs)
def load_list_of_payment_methods(self, *args, **kwargs):
return self.s.get("{}/v3/wallet/card".format(self.root_url), *args, **kwargs)

19
parler/wallet_general.py Normal file
View File

@ -0,0 +1,19 @@
class WalletGeneralAPI:
def __init__(self, session, root_url="http://api.parler.com"):
self.root_url = root_url
self.s = session
def internal_transfer_funds(self, *args, **kwargs):
return self.s.post("{}/v3/wallet/transfer".format(self.root_url), *args, **kwargs)
def load_billing_user(self, *args, **kwargs):
return self.s.get("{}/v3/billing".format(self.root_url), *args, **kwargs)
def set_tipping_enabled(self, *args, **kwargs):
return self.s.post("{}/v3/billing/tipping".format(self.root_url), *args, **kwargs)
def submit_new_billing_address(self, *args, **kwargs):
return self.s.post("{}/v3/billing/address".format(self.root_url), *args, **kwargs)
def submit_w9_with_file(self, *args, **kwargs):
return self.s.post("{}/v3/billing/w9".format(self.root_url), *args, **kwargs)