diff --git a/README.md b/README.md index 1d82202..b4d0f13 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ client = InstaClient(driver_path='/chromedriver.exe') from instaclient.errors import * try: - client.login(username=username, password=password) # Go through Login Procedure + client_login(username=username, password=password) # Go through Login Procedure except VerificationCodeNecessary: # This error is raised if the user has 2FA turned on. code = input('Enter the 2FA security code generated by your Authenticator App or sent to you by SMS') diff --git a/instaclient/client/auth.py b/instaclient/client/auth.py index 4b9fa40..b0c101c 100644 --- a/instaclient/client/auth.py +++ b/instaclient/client/auth.py @@ -1,13 +1,14 @@ from instaclient.client import * +from instaclient.client.checker import Checker from instaclient.client.component import Component if TYPE_CHECKING: from instaclient.client.instaclient import InstaClient -class Auth(Component): +class Auth(Checker): @Component._manage_driver(login=False) - def login(self:'InstaClient', username:str, password:str, check_user:bool=True, _discard_driver:bool=False): + def _login(self:'InstaClient', username:str, password:str, check_user:bool=True, _discard_driver:bool=False): """ Sign Into Instagram with credentials. Go through 2FA if necessary. Sets the InstaClient variable `InstaClient.logged_in` to True if login was successful. @@ -32,14 +33,13 @@ def login(self:'InstaClient', username:str, password:str, check_user:bool=True, # Attempt Login self.driver.get(ClientUrls.LOGIN_URL) LOGGER.debug('INSTACLIENT: Got Login Page') - # Detect Cookies Dialogue - if self._check_existence( EC.presence_of_element_located((By.XPATH, Paths.COOKIES_LINK))): - self._dismiss_cookies() + # Detect Cookies Dialogue + self._dismiss_cookies() # Get Form elements - username_input = self._check_existence( EC.presence_of_element_located((By.XPATH,Paths.USERNAME_INPUT)), url=ClientUrls.LOGIN_URL) - password_input = self._check_existence( EC.presence_of_element_located((By.XPATH,Paths.PASSWORD_INPUT)), url=ClientUrls.LOGIN_URL) + username_input = self._find_element( EC.presence_of_element_located((By.XPATH,Paths.USERNAME_INPUT)), url=ClientUrls.LOGIN_URL) + password_input = self._find_element( EC.presence_of_element_located((By.XPATH,Paths.PASSWORD_INPUT)), url=ClientUrls.LOGIN_URL) LOGGER.debug('INSTACLIENT: Found elements') # Fill out form username_input.send_keys(username) @@ -47,7 +47,7 @@ def login(self:'InstaClient', username:str, password:str, check_user:bool=True, password_input.send_keys(password) time.sleep(1) LOGGER.debug('INSTACLIENT: Filled in form') - login_btn = self._check_existence( EC.presence_of_element_located((By.XPATH,Paths.LOGIN_BTN)), url=ClientUrls.LOGIN_URL)# login button xpath changes after text is entered, find first + login_btn = self._find_element( EC.presence_of_element_located((By.XPATH,Paths.LOGIN_BTN)), url=ClientUrls.LOGIN_URL)# login button xpath changes after text is entered, find first self._press_button(login_btn) LOGGER.debug('INSTACLIENT: Sent form') except ElementClickInterceptedException as error: @@ -117,17 +117,15 @@ def login(self:'InstaClient', username:str, password:str, check_user:bool=True, self.driver.get(ClientUrls.HOME_URL) # Detect 'Save to Home Screen' Dialogue - if self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.DISMISS_DIALOGUE))): - self._dismiss_dialogue() + self._dismiss_dialogue() # Detect 'Turn On Notifications' Box - if self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.DISMISS_DIALOGUE))): - self._dismiss_dialogue() + self._dismiss_dialogue() return self.logged_in @Component._manage_driver(login=False) - def resend_security_code(self): + def _resend_security_code(self): """ Resend security code if code hasn't been sent successfully. The code is used to verify the login attempt if `instaclient.errors.common.SuspiciousLoginAttemptError` is raised. @@ -161,9 +159,9 @@ def resend_security_code(self): @Component._manage_driver(login=False) - def input_security_code(self, code:int or str, _discard_driver:bool=False): + def _input_security_code(self:'InstaClient', code:int, _discard_driver:bool=False): """ - Complete login procedure started with `InstaClient.login()` and insert security code required if `instaclient.errors.common.SuspiciousLoginAttemptError` is raised. Sets `InstaClient.logged_in` attribute to True if login was successful. + Complete login procedure started with `InstaClient_login()` and insert security code required if `instaclient.errors.common.SuspiciousLoginAttemptError` is raised. Sets `InstaClient.logged_in` attribute to True if login was successful. Args: code (intorstr): The security code sent by Instagram via SMS or email. @@ -199,9 +197,9 @@ def input_security_code(self, code:int or str, _discard_driver:bool=False): @Component._manage_driver(login=False) - def input_verification_code(self, code:int or str, _discard_driver:bool=False): + def _input_verification_code(self, code:int, _discard_driver:bool=False): """ - Complete login procedure started with `InstaClient.login()` and insert 2FA security code. Sets `instaclient.logged_in` to True if login was successful. + Complete login procedure started with `InstaClient_login()` and insert 2FA security code. Sets `instaclient.logged_in` to True if login was successful. Args: code (int|str): The 2FA security code generated by the Authenticator App or sent via SMS to the user. @@ -232,7 +230,7 @@ def input_verification_code(self, code:int or str, _discard_driver:bool=False): @Component._manage_driver(login=False) - def logout(self, _discard_driver:bool=False): + def _logout(self:'InstaClient', _discard_driver:bool=False): """ Check if the client is currently connected to Instagram and logs of the current InstaClient session. diff --git a/instaclient/client/checker.py b/instaclient/client/checker.py index 9911a80..76e0f65 100644 --- a/instaclient/client/checker.py +++ b/instaclient/client/checker.py @@ -24,10 +24,9 @@ def _check_status(self: 'InstaClient', _discard_driver:bool=False): LOGGER.debug(self.driver.current_url) if ClientUrls.HOME_URL not in self.driver.current_url: self.driver.get(ClientUrls.HOME_URL) - if self._check_existence( EC.presence_of_element_located((By.XPATH, Paths.COOKIES_LINK))): - self._dismiss_cookies() + self._dismiss_cookies() if self._check_existence( EC.presence_of_element_located((By.XPATH, Paths.NOT_NOW_BTN))): - btn = self._check_existence( EC.presence_of_element_located((By.XPATH, Paths.NOT_NOW_BTN))) + btn = self._find_element( EC.presence_of_element_located((By.XPATH, Paths.NOT_NOW_BTN))) self._press_button(btn) LOGGER.debug('INSTACLIENT: Dismissed dialogue') @@ -81,8 +80,7 @@ def _is_valid_user(self:'InstaClient', user:str, nav_to_user:bool=True, _discard time.sleep(1) - if self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.COOKIES_LINK))): - self._dismiss_cookies() + self._dismiss_cookies() element = self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.PAGE_NOT_FOUND)), wait_time=3) if element: diff --git a/instaclient/client/component.py b/instaclient/client/component.py index 4406902..3f5eb67 100644 --- a/instaclient/client/component.py +++ b/instaclient/client/component.py @@ -19,7 +19,7 @@ def wrapper(self: 'InstaClient', *args, **kwargs): error = False result = None try: - result = func(*args, **kwargs) + result = func(self, *args, **kwargs) time.sleep(1) except Exception as exception: error = exception @@ -41,7 +41,6 @@ def wrapper(self: 'InstaClient', *args, **kwargs): return outer - @staticmethod def _discard_driver(self: 'InstaClient'): LOGGER.debug('INSTACLIENT: Discarding driver...') if self.driver: @@ -51,7 +50,6 @@ def _discard_driver(self: 'InstaClient'): LOGGER.debug('INSTACLIENT: Driver Discarded') - @staticmethod def _init_driver(self: 'InstaClient', login=False, retries=0, func=None): LOGGER.debug('INSTACLIENT: Initiating Driver | attempt {} | func: {}'.format(retries, func)) try: @@ -99,7 +97,7 @@ def _init_driver(self: 'InstaClient', login=False, retries=0, func=None): if login: try: - self.login(self.username, self.password) + self._login(self.username, self.password) except: raise InstaClientError(message='Tried logging in when initiating driver, but username and password are not defined.') @@ -139,11 +137,9 @@ def _find_element(self:'InstaClient', expectation, url:str=None, wait_time:int=5 LOGGER.debug('Retrying find element...') if attempt == 0: LOGGER.debug('Checking for cookies/dialogues...') - if self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.COOKIES_LINK))): - self._dismiss_cookies() + self._dismiss_cookies() - if self._check_existence(EC.presence_of_element_located((By.XPATH, Paths.DISMISS_DIALOGUE))): - self._dismiss_dialogue() + self._dismiss_dialogue() return self._find_element(expectation, url, wait_time=2, attempt=attempt+1) elif retry: LOGGER.debug('Checking if user is logged in...') @@ -172,7 +168,6 @@ def _find_element(self:'InstaClient', expectation, url:str=None, wait_time:int=5 raise NoSuchElementException() - @staticmethod def _check_existence(self:'InstaClient', expectation, wait_time:int=2): """ Checks if an element exists. diff --git a/instaclient/client/instaclient.py b/instaclient/client/instaclient.py index b8243bd..6b9de61 100644 --- a/instaclient/client/instaclient.py +++ b/instaclient/client/instaclient.py @@ -63,14 +63,68 @@ def __init__(self, driver_type: int=CHROMEDRIVER, host_type:int=LOCAHOST, driver if init_driver: self._init_driver(func='__init__') + # AUTH + def login(self: 'InstaClient', username: str, password: str, check_user: bool=True, _discard_driver: bool=False): + return super()._login(username, password, check_user=check_user, _discard_driver=_discard_driver) + + def resend_security_code(self): + return super()._resend_security_code() + + def input_security_code(self, code: int, _discard_driver: bool=False): + return super()._input_security_code(code, _discard_driver=_discard_driver) + + def input_verification_code(self, code: int, _discard_driver: bool=False): + return super()._input_verification_code(code, _discard_driver=_discard_driver) + + def logout(self: 'InstaClient', _discard_driver: bool=False): + return super()._logout(_discard_driver=_discard_driver) + # CHECKERS + def check_status(self: 'InstaClient', _discard_driver: bool=False) -> bool: + return super()._check_status(_discard_driver=_discard_driver) + + def is_valid_user(self: 'InstaClient', user: str, nav_to_user: bool=True, _discard_driver: bool=False) -> bool: + return super()._is_valid_user(user, nav_to_user=nav_to_user, _discard_driver=_discard_driver) - # SCRAPING + # SCRAPING + def get_notifications(self: 'InstaClient', types: Optional[list]=None, count: Optional[int]=None) -> Optional[list]: + return super()._scrape_notifications(types=types, count=count) + + def get_profile(self: 'InstaClient', username: str, context: bool=True) -> Optional[Profile]: + return super()._scrape_profile(username, context=context) + + def get_user_images(self, user: str, _discard_driver: bool=False): + return super()._scrape_user_images(user, _discard_driver=_discard_driver) + + def get_followers(self, user: str, count: int, check_user:bool=True, _discard_driver:bool=False, callback_frequency: int=100, callback=None, **callback_args) -> Optional[list]: + return super()._scrape_followers(user, count, check_user=check_user, _discard_driver=_discard_driver, callback_frequency=callback_frequency, callback=callback, **callback_args) + + def get_hashtag(self: 'InstaClient', tag: str) -> Optional[Hashtag]: + return super()._scrape_tag(tag, self.username) + + + + # INTERACTIONS + def follow(self, user: str, nav_to_user: bool=True, _discard_driver: bool=False): + return super()._follow_user(user, nav_to_user=nav_to_user, _discard_driver=_discard_driver) + + def unfollow(self, user: str, nav_to_user:bool=True, check_user:bool=True, _discard_driver: bool=False): + return super()._unfollow_user(user, nav_to_user=nav_to_user, check_user=check_user, _discard_driver=_discard_driver) + + def send_dm(self, user: str, message: str, _discard_driver: bool=False): + return super()._send_dm(user, message, _discard_driver=_discard_driver) + + def like_user_posts(self, user: str, n_posts: int, like: bool=True, _discard_driver: bool=False): + return super()._like_latest_posts(user, n_posts, like=like, _discard_driver=_discard_driver) + + def like_feed_posts(self, count:int): + return super()._like_feed_posts(count) + def scroll(self, mode:int=Interactions.PAGE_DOWN_SCROLL, size:int=500, times:int=1, interval:int=3): + return super()._scroll(mode=mode, size=size, times=times, interval=interval) - # INTERACTIONS diff --git a/instaclient/client/interactions.py b/instaclient/client/interactions.py index e334128..101a24b 100644 --- a/instaclient/client/interactions.py +++ b/instaclient/client/interactions.py @@ -1,8 +1,9 @@ from instaclient.client import * from instaclient.client.component import Component +from instaclient.client.navigator import Navigator -class Interactions(Component): +class Interactions(Navigator): PIXEL_SCROLL=3 END_PAGE_SCROLL=4 PAGE_DOWN_SCROLL=5 @@ -23,7 +24,7 @@ def _follow_user(self, user:str, nav_to_user:bool=True, _discard_driver:bool=Fal """ # Navigate to User Page if nav_to_user: - self.nav_user(user, check_user=False) + self._nav_user(user, check_user=False) # Check User Vadility try: @@ -64,7 +65,7 @@ def _unfollow_user(self, user:str, nav_to_user=True, check_user=True, _discard_d InvalidUserError: raised if the user specified by the `user` argument is invalid. """ if nav_to_user: - self.nav_user(user, check_user) + self._nav_user(user, check_user) elif check_user: try: self._is_valid_user(user, nav_to_user=False) @@ -101,7 +102,7 @@ def _like_latest_posts(self, user:str, n_posts:int, like:bool=True, _discard_dri action = 'Like' if like else 'Unlike' - self.nav_user(user) + self._nav_user(user) imgs = [] elements = self._find_element(EC.presence_of_all_elements_located((By.CLASS_NAME, '_9AhH0'))) @@ -133,7 +134,7 @@ def _send_dm(self, user:str, message:str, _discard_driver:bool=False): """ # Navigate to User's dm page try: - self.nav_user_dm(user) + self._nav_user_dm(user) text_area = self._find_element(EC.presence_of_element_located((By.XPATH, Paths.DM_TEXT_AREA))) text_area.send_keys(message) time.sleep(1) diff --git a/instaclient/client/navigator.py b/instaclient/client/navigator.py index 789ef18..95950f5 100644 --- a/instaclient/client/navigator.py +++ b/instaclient/client/navigator.py @@ -41,7 +41,7 @@ def _nav_user_dm(self:'InstaClient', user:str, check_user:bool=True): True if operation was successful """ try: - self.nav_user(user, check_user=check_user) + self._nav_user(user, check_user=check_user) private = False LOGGER.debug('INSTACLIENT: User <{}> is valid and public (or followed)'.format(user)) except PrivateAccountError: diff --git a/instaclient/client/scraper.py b/instaclient/client/scraper.py index 807e896..c13f278 100644 --- a/instaclient/client/scraper.py +++ b/instaclient/client/scraper.py @@ -1,4 +1,5 @@ import requests, json +from instaclient import client from instaclient.client import * if TYPE_CHECKING: @@ -9,30 +10,9 @@ class Scraper(Component): TYPES = [InstaBaseObject.GRAPH_IMAGE, InstaBaseObject.GRAPH_VIDEO, InstaBaseObject.GRAPH_SIDECAR] - # SCRAPE HASHTAG - @Component._manage_driver(login=False) - def _scrape_tag(self:'InstaClient', tag:str, viewer:str): - LOGGER.debug('INSTACLIENT: scrape hashtag') - result = self._request(GraphUrls.GRAPH_TAGS.format(tag)) - - try: - data = result['graphql']['hashtag'] - tag:Hashtag = Hashtag( - id=data['id'], - viewer=viewer, - name=data['name'], - count=data['edge_hashtag_to_media']['count'], - posts_data=data['edge_hashtag_to_media'] - ) - LOGGER.info('Scraped hashtag: {}'.format(tag)) - return tag - except: - raise InvalidInstaSchemaError(__name__) - - - # SCRAPE NOTIFICATIONS + # SCRAPE USER DATA @Component._manage_driver() - def _scrape_notifications(self:'InstaClient', viewer:str, types:list=None, count:int=None) -> list: + def _scrape_notifications(self:'InstaClient', types:Optional[list], count:int=None) -> Optional[list]: if types is None or len(types) == 0: types = [InstaBaseObject.GRAPH_FOLLOW, InstaBaseObject.GRAPH_LIKE, InstaBaseObject.GRAPTH_TAGGED, InstaBaseObject.GRAPH_COMMENT, InstaBaseObject.GRAPH_MENTION] @@ -69,14 +49,16 @@ def _scrape_notifications(self:'InstaClient', viewer:str, types:list=None, count # Map nodes into Notification Objects for node in nodes: user = Profile( + client=self, id=node['user']['id'], - viewer=viewer, + viewer=self.username, username=node['user']['username'], name=node['user']['full_name'] ) notifications.append(Notification( + client=self, id=node['id'], - viewer=viewer, + viewer=self.username, from_user=user, type=node['__typename'], timestamp=node['timestamp'], @@ -84,12 +66,11 @@ def _scrape_notifications(self:'InstaClient', viewer:str, types:list=None, count return notifications - # USER DATA PRODECURES @Component._manage_driver(login=False) - def _scrape_profile(self:'InstaClient', username:str, login:bool=True) -> Optional[Profile]: + def _scrape_profile(self:'InstaClient', username:str, context:bool=True) -> Optional[Profile]: - if login and not self.logged_in and None not in (self.username, self.password): - self.login(self.username, self.password) + if context and not self.logged_in and None not in (self.username, self.password): + self._login(self.username, self.password) data = self._request(GraphUrls.GRAPH_USER.format(username), use_driver=True) if not data: @@ -141,7 +122,7 @@ def _scrape_user_images(self, user:str, _discard_driver:bool=False): """ - self.nav_user(user) + self._nav_user(user) img_srcs = [] finished = False @@ -157,7 +138,7 @@ def _scrape_user_images(self, user:str, _discard_driver:bool=False): @Component._manage_driver(login=False) - def _scrape_followers(self, user:str, count:int, check_user=True, _discard_driver=False, callback_frequency:int=100, callback=None, **callback_args): + def _scrape_followers(self, user:str, count:int, check_user=True, _discard_driver=False, callback_frequency:int=100, callback=None, **callback_args) -> Optional[list]: """ scrape_followers: Scrape an instagram user's followers and return them as a list of strings. @@ -177,7 +158,7 @@ def _scrape_followers(self, user:str, count:int, check_user=True, _discard_drive PrivateAccountError: Raised if the user is a private account NoSuchElementException: Raised if an element is not found when compiling operation. """ - self.nav_user(user, check_user=check_user) + self._nav_user(user, check_user=check_user) followers_btn:WebElement = self._find_element(EC.presence_of_element_located((By.XPATH, Paths.FOLLOWERS_BTN)), url=ClientUrls.NAV_USER.format(user)) # Click followers btn self._press_button(followers_btn) @@ -243,7 +224,31 @@ def _scrape_followers(self, user:str, count:int, check_user=True, _discard_drive LOGGER.debug(f'Failed: {len(failed)}') return followers + # SCRAPE HASHTAG + + + # SCRAPE HASHTAG + @Component._manage_driver(login=False) + def _scrape_tag(self:'InstaClient', tag:str, viewer:str) -> Optional[Hashtag]: + LOGGER.debug('INSTACLIENT: scrape hashtag') + result = self._request(GraphUrls.GRAPH_TAGS.format(tag)) + + try: + data = result['graphql']['hashtag'] + tag:Hashtag = Hashtag( + id=data['id'], + viewer=viewer, + name=data['name'], + count=data['edge_hashtag_to_media']['count'], + posts_data=data['edge_hashtag_to_media'] + ) + LOGGER.info('Scraped hashtag: {}'.format(tag)) + return tag + except: + raise InvalidInstaSchemaError(__name__) + + # GENERAL TOOL def _request(self: 'InstaClient', url:str, use_driver:bool=False) -> Optional[dict]: if not use_driver: if self.proxy: diff --git a/instaclient/instagram/instaobject.py b/instaclient/instagram/instaobject.py index 4cc139d..739db51 100644 --- a/instaclient/instagram/instaobject.py +++ b/instaclient/instagram/instaobject.py @@ -1,6 +1,7 @@ import abc, json -import instaclient.client.instaclient as client -from instaclient.utilities import get_url +from typing import TYPE_CHECKING +if TYPE_CHECKING: + import instaclient.client.instaclient as client class InstaBaseObject(abc.ABC): GRAPH_IMAGE = 'GraphImage' @@ -15,7 +16,7 @@ class InstaBaseObject(abc.ABC): GRAPH_MENTION = 'GraphMentionStory' GRAPH_COMMENT = 'GraphCommentMediaStory' - def __init__(self, id:str, type:str, viewer:str=None, client:'client.InstaClient'=None): + def __init__(self, client:'client.InstaClient', id:str, type:str, viewer:str=None): """ Reppresents an abstract instagram object. @@ -25,11 +26,10 @@ def __init__(self, id:str, type:str, viewer:str=None, client:'client.InstaClient type (str): Instagram object type. Can be `GRAPTH_IMAGE`, `GRAPH_VIDEO`, `GRAPH_SIDECAR`, `GRAPH_PROFILE`, `GRAPH_HASHTAG` """ # Required + self.client = client self.id = id - self.type = type - # Optional self.viewer = viewer - self.client = client + self.type = type def __str__(self) -> str: return str(self.to_dict()) @@ -37,7 +37,6 @@ def __str__(self) -> str: def __getitem__(self, item: str): return self.__dict__[item] - def de_json(cls, data: str, client: 'client.InstaClient'): if not data: @@ -85,6 +84,3 @@ def get_viewer(self): def get_type(self): return self.type - def _get_url(self, url): - return get_url(url, self.scraperapi_key) - diff --git a/instaclient/instagram/notification.py b/instaclient/instagram/notification.py index 02383da..8c4543c 100644 --- a/instaclient/instagram/notification.py +++ b/instaclient/instagram/notification.py @@ -1,10 +1,11 @@ -from instaclient.classes.baseprofile import BaseProfile -from instaclient.classes.instaobject import InstaBaseObject +from instaclient.instagram.profile import Profile +from instaclient.instagram.instaobject import InstaBaseObject class Notification(InstaBaseObject): - def __init__(self, id:str, viewer:int or BaseProfile, from_user:BaseProfile, type:str, timestamp): - super().__init__(id, viewer, type) + def __init__(self, client, id:str, viewer:str, from_user:Profile, type:str, timestamp): + super().__init__(client, id, type, viewer) + # Required self.from_user = from_user self.timestamp = timestamp diff --git a/instaclient/instagram/post.py b/instaclient/instagram/post.py index 62c704b..7c52d34 100644 --- a/instaclient/instagram/post.py +++ b/instaclient/instagram/post.py @@ -1,9 +1,15 @@ from instaclient.errors.common import InvalidInstaRequestError, InvalidInstaSchemaError -from requests.models import InvalidURL from instaclient.client.constants import GraphUrls +# Objects from instaclient.instagram.instaobject import InstaBaseObject from instaclient.instagram.profile import Profile +from instaclient import LOGGER +# Others import requests +from typing import TYPE_CHECKING, Type + +if TYPE_CHECKING: + from instaclient.client.instaclient import InstaClient class Post(InstaBaseObject): @@ -11,6 +17,7 @@ def __init__(self, # Required id:str, type:str, + client:'InstaClient', # Optional viewer:Profile=None, text:str=None, @@ -19,7 +26,7 @@ def __init__(self, id = id type = self.index_type(type) - super().__init__(id=id, type=type, viewer=viewer) + super().__init__(id=id, type=type, viewer=viewer, client=client) self.text = text self.shortcode = shortcode diff --git a/instaclient/instagram/profile.py b/instaclient/instagram/profile.py index e535a0b..a67e35f 100644 --- a/instaclient/instagram/profile.py +++ b/instaclient/instagram/profile.py @@ -1,6 +1,8 @@ -from typing import Optional -from instaclient.client.instaclient import InstaClient +from typing import Optional, TYPE_CHECKING import requests, logging + +if TYPE_CHECKING: + from instaclient.client.instaclient import InstaClient from instaclient.errors.common import InvalidInstaRequestError, InvalidInstaSchemaError from instaclient.client.constants import GraphUrls from instaclient.instagram.instaobject import InstaBaseObject @@ -11,9 +13,9 @@ class Profile(InstaBaseObject): def __init__(self, client:'InstaClient', - username:str, id:str, - viewer:str=None, + viewer:str, + username:str, name:str=None, biography:str=None, is_private:bool=None, @@ -34,8 +36,7 @@ def __init__(self, mutual_followed:bool=None, requested_by_viewer:bool=None, ): - - super().__init__(id=id, type=self.GRAPH_PROFILE, viewer=viewer, client=client) + super().__init__(client, id, self.GRAPH_PROFILE, viewer) # Required self.username = username # Optional diff --git a/setup.py b/setup.py index 28b5bcd..03999f7 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name = 'instaclient', # How you named your package folder (MyLib) packages = find_packages(exclude=['tests, drivers']), # Chose the same as "name" - version = '2.3', # Start with a small number and increase it with every change you make + version = '2.4', # Start with a small number and increase it with every change you make license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository description = 'Instagram client built with Python 3.8 and the Selenium package.', long_description=README, @@ -19,7 +19,7 @@ author = 'David Wicker', # Type in your name author_email = 'davidwickerhf@gmail.com', # Type in your E-Mail url = 'https://github.com/wickerdevs/py-instaclient', # Provide either the link to your github or to your website - download_url = 'https://github.com/wickerdevs/py-instaclient/archive/v2.3.tar.gz', # I explain this later on + download_url = 'https://github.com/wickerdevs/py-instaclient/archive/v2.4.tar.gz', # I explain this later on keywords = ['INSTAGRAM', 'BOT', 'INSTAGRAM BOT', 'INSTAGRAM CLIENT'], # Keywords that define your package best install_requires=[ # I get to this in a second 'selenium', diff --git a/tests/tests.py b/tests/tests.py index b07a4cd..a925879 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -24,7 +24,7 @@ def test_login(self): Test Class login() """ try: - response = self.client.login() + response = self.client_login() except VerificationCodeNecessary: code = input('Enter the 2FA security code sent to your phone or generated by your Authenticator App:') try: