Skip to content

Commit

Permalink
Editing Bio [#8] Added .account_info, .account_edit, .account_change_…
Browse files Browse the repository at this point in the history
…picture and tests for it
  • Loading branch information
adw0rd committed Nov 6, 2020
1 parent afb1c55 commit d739c04
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 25 deletions.
42 changes: 23 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ The current types are in [types.py](/instagrapi/types.py):
| Media | Media (Photo, Video, Album, IGTV or Story) |
| Resource | Part of Media (for albums) |
| MediaOembed | Short version of Media |
| User | User data |
| UserShort | Short user data (stored in Usertag, Comment, Media, Direct) |
| Account | Full private info for your account (e.g. email, phone_number) |
| User | Full public user data |
| UserShort | Short public user data (stored in Usertag, Comment, Media, Direct) |
| Usertag | Tag user in Media (coordinates + UserShort) |
| Location | GEO location (GEO coordinates, name, address) |
| Collection | Collection of medias (name, picture and list of medias) |
Expand All @@ -84,20 +85,23 @@ The current types are in [types.py](/instagrapi/types.py):

This is your authorized account

| Method | Return | Description |
| -------------------------------------------------------- | -------- | ------------------------------------------------------------- |
| Client(settings: dict = {}, proxy: str = "") | bool | Login by username and password |
| login(username: str, password: str) | bool | Login by username and password |
| relogin() | bool | Relogin with clean cookies (required cl.username/cl.password) |
| login_by_sessionid(sessionid: str) | bool | Login by sessionid from Instagram site |
| get_settings() | dict | Return settings dict (more details below) |
| set_proxy(dsn: str) | dict | Support socks and http/https proxy |
| cookie_dict | dict | Return cookies |
| user_id | int | Return your user_id (after login) |
| device | dict | Return device dict which we pass to Instagram |
| set_device(device: dict) | bool | Change device settings |
| set_user_agent(user_agent: str = "") | bool | Change User-Agent header |
| base_headers | dict | Base headers for Instagram |
| Method | Return | Description |
| -------------------------------------------------------- | --------- | ------------------------------------------------------------- |
| Client(settings: dict = {}, proxy: str = "") | bool | Login by username and password |
| login(username: str, password: str) | bool | Login by username and password |
| relogin() | bool | Relogin with clean cookies (required cl.username/cl.password) |
| login_by_sessionid(sessionid: str) | bool | Login by sessionid from Instagram site |
| get_settings() | dict | Return settings dict (more details below) |
| set_proxy(dsn: str) | dict | Support socks and http/https proxy |
| cookie_dict | dict | Return cookies |
| user_id | int | Return your user_id (after login) |
| device | dict | Return device dict which we pass to Instagram |
| set_device(device: dict) | bool | Change device settings |
| set_user_agent(user_agent: str = "") | bool | Change User-Agent header |
| base_headers | dict | Base headers for Instagram |
| account_info() | Account | Get private info for your account (e.g. email, phone_number) |
| account_edit(**data) | Account | Change profile data (e.g. external_url, phone_number, username, first_name (full_name), biography, email) |
| account_change_picture(path: path) | UserShort | Change Profile picture |

Example:

Expand Down Expand Up @@ -163,7 +167,7 @@ Viewing and editing publications (medias)
| media_delete(media_pk: int) | bool | Delete media |
| media_edit(media_pk: int, caption: str, title: str, usertags: List[Usertag], location: Location) | dict | Change caption for media |
| media_user(media_pk: int) | User | Get user info for media |
| media_oembed(url: str) | ShortMedia | Return short media info by media URL |
| media_oembed(url: str) | MediaOembed | Return short media info by media URL |
| media_comment(media_id: str, message: str) | bool | Write message to media |
| media_comments(media_id: str) | List\[Comment] | Get all comments |

Expand Down Expand Up @@ -208,8 +212,8 @@ View a list of a user's medias, following and followers
| Method | Return | Description |
| -------------------------------------------------- | ------------------- | ------------------------------------------------ |
| user_medias(user_id: int, amount: int = 20) | List\[Media] | Get list of medias by user_id |
| user_followers(user_id: int) | Dict\[int: User] | Get dict of followers users |
| user_following(user_id: int) | Dict\[int: User] | Get dict of following users |
| user_followers(user_id: int) | Dict\[int, User] | Get dict of followers users |
| user_following(user_id: int) | Dict\[int, User] | Get dict of following users |
| user_info(user_id: int) | User | Get user info |
| user_info_by_username(username: str) | User | Get user info by username |
| user_follow(user_id: int) | bool | Follow user |
Expand Down
37 changes: 35 additions & 2 deletions instagrapi/account.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import requests

from pathlib import Path
from json.decoder import JSONDecodeError

from .exceptions import ClientLoginRequired, ClientError
from .extractors import extract_account, extract_user_short
from .types import Account, UserShort
from .utils import gen_csrftoken


Expand All @@ -22,7 +24,7 @@ def reset_password(self, username):
"Accept": "*/*",
"Accept-Encoding": "gzip,deflate",
"Accept-Language": "en-US",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15"
},
proxies=self.public.proxies
)
Expand All @@ -32,3 +34,34 @@ def reset_password(self, username):
if "/login/" in response.url:
raise ClientLoginRequired(e, response=response)
raise ClientError(e, response=response)

def account_info(self) -> Account:
result = self.private_request('accounts/current_user/?edit=true')
return extract_account(result['user'])

def account_edit(self, **data) -> Account:
"""Edit your profile (authorized account)
"""
fields = ("external_url", "phone_number", "username", "first_name", "biography", "email")
data = {key: val for key, val in data.items() if key in fields}
if 'email' not in data and 'phone_number' not in data:
# Instagram Error: You need an email or confirmed phone number.
user_data = self.account_info().dict()
user_data['first_name'] = user_data.pop('full_name') # instagram :)
user_data = {field: user_data[field] for field in fields}
data = dict(user_data, **data)
result = self.private_request(
"accounts/edit_profile/",
self.with_default_data(data)
)
return extract_account(result["user"])

def account_change_picture(self, path: Path) -> UserShort:
"""Change photo for your profile (authorized account)
"""
upload_id, _, _ = self.photo_rupload(Path(path))
result = self.private_request(
"accounts/change_profile_picture/",
self.with_default_data({'use_fbuploader': True, 'upload_id': upload_id})
)
return extract_user_short(result["user"])
6 changes: 5 additions & 1 deletion instagrapi/extractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .types import (
Media, Resource, User, UserShort, Usertag,
Location, Collection, Comment, MediaOembed,
DirectThread, DirectMessage
DirectThread, DirectMessage, Account
)


Expand Down Expand Up @@ -217,3 +217,7 @@ def extract_direct_message(data):
if 'media_share' in data:
data['media_share'] = extract_media_v1(data['media_share'])
return DirectMessage(**data)


def extract_account(data):
return Account(**data)
16 changes: 16 additions & 0 deletions instagrapi/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ class User(BaseModel):
is_business: bool


class Account(BaseModel):
pk: int
username: str
full_name: str
is_private: bool
profile_pic_url: HttpUrl
is_verified: bool
biography: Optional[str] = ''
external_url: Optional[HttpUrl]
is_business: bool
birthday: Optional[str]
phone_number: Optional[str]
gender: Optional[int]
email: Optional[str]


class UserShort(BaseModel):
pk: int
username: str
Expand Down
4 changes: 2 additions & 2 deletions instagrapi/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def user_following_v1(self, user_id: int, amount: int = 0) -> list:
users = users[:amount]
return users

def user_following(self, user_id: int, use_cache: bool = True, amount: int = 0) -> Dict[int: User]:
def user_following(self, user_id: int, use_cache: bool = True, amount: int = 0) -> Dict[int, User]:
"""Return dict {user_id: user} of following users
"""
user_id = int(user_id)
Expand Down Expand Up @@ -213,7 +213,7 @@ def user_followers_v1(self, user_id: int, amount: int = 0) -> list:
break
return users

def user_followers(self, user_id: int, use_cache: bool = True, amount: int = 0) -> Dict[int: User]:
def user_followers(self, user_id: int, use_cache: bool = True, amount: int = 0) -> Dict[int, User]:
"""Return dict {user_id: user} of followers users
"""
user_id = int(user_id)
Expand Down
35 changes: 34 additions & 1 deletion tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from instagrapi import Client
from instagrapi.types import (
User, UserShort, Media, MediaOembed, Comment, Collection,
DirectThread, DirectMessage, Usertag, Location
DirectThread, DirectMessage, Usertag, Location, Account
)


Expand Down Expand Up @@ -644,5 +644,38 @@ def test_direct_thread(self):
self.assertEqual(ping.thread_id, pong.thread_id)


class ClientAccountTestCase(ClientPrivateTestCase):

def test_account_edit(self):
# current
one = self.api.user_info(self.api.user_id)
self.assertIsInstance(one, User)
# change
url = 'https://trotiq.com/'
two = self.api.account_edit(external_url=url)
self.assertIsInstance(two, Account)
self.assertEqual(str(two.external_url), url)
# return back
three = self.api.account_edit(external_url=one.external_url)
self.assertIsInstance(three, Account)
self.assertEqual(one.external_url, three.external_url)

def test_account_change_picture(self):
# current
one = self.api.user_info(self.api.user_id)
self.assertIsInstance(one, User)
dhbastards = self.api.user_info_by_username('dhbastards')
# change
two = self.api.account_change_picture(
self.api.photo_download_by_url(dhbastards.profile_pic_url)
)
self.assertIsInstance(two, UserShort)
# return back
three = self.api.account_change_picture(
self.api.photo_download_by_url(one.profile_pic_url)
)
self.assertIsInstance(three, UserShort)


if __name__ == '__main__':
unittest.main()

0 comments on commit d739c04

Please sign in to comment.