#!/usr/bin/env python # -*- coding: utf-8 -*- import requests import random import json import hashlib import hmac import urllib import uuid import time import copy import math import sys from datetime import datetime import calendar import os #The urllib library was split into other modules from Python 2 to Python 3 if sys.version_info.major == 3: import urllib.parse from ImageUtils import getImageSize from requests_toolbelt import MultipartEncoder from moviepy.editor import VideoFileClip class InstagramAPI: API_URL = 'https://i.instagram.com/api/v1/' DEVICE_SETTINTS = { 'manufacturer' : 'Xiaomi', 'model' : 'HM 1SW', 'android_version' : 18, 'android_release' : '4.3' } USER_AGENT = 'Instagram 9.2.0 Android ({android_version}/{android_release}; 320dpi; 720x1280; {manufacturer}; {model}; armani; qcom; en_US)'.format(**DEVICE_SETTINTS) IG_SIG_KEY = '012a54f51c49aa8c5c322416ab1410909add32c966bbaa0fe3dc58ac43fd7ede' EXPERIMENTS = 'ig_android_progressive_jpeg,ig_creation_growth_holdout,ig_android_report_and_hide,ig_android_new_browser,ig_android_enable_share_to_whatsapp,ig_android_direct_drawing_in_quick_cam_universe,ig_android_huawei_app_badging,ig_android_universe_video_production,ig_android_asus_app_badging,ig_android_direct_plus_button,ig_android_ads_heatmap_overlay_universe,ig_android_http_stack_experiment_2016,ig_android_infinite_scrolling,ig_fbns_blocked,ig_android_white_out_universe,ig_android_full_people_card_in_user_list,ig_android_post_auto_retry_v7_21,ig_fbns_push,ig_android_feed_pill,ig_android_profile_link_iab,ig_explore_v3_us_holdout,ig_android_histogram_reporter,ig_android_anrwatchdog,ig_android_search_client_matching,ig_android_high_res_upload_2,ig_android_new_browser_pre_kitkat,ig_android_2fac,ig_android_grid_video_icon,ig_android_white_camera_universe,ig_android_disable_chroma_subsampling,ig_android_share_spinner,ig_android_explore_people_feed_icon,ig_explore_v3_android_universe,ig_android_media_favorites,ig_android_nux_holdout,ig_android_search_null_state,ig_android_react_native_notification_setting,ig_android_ads_indicator_change_universe,ig_android_video_loading_behavior,ig_android_black_camera_tab,liger_instagram_android_univ,ig_explore_v3_internal,ig_android_direct_emoji_picker,ig_android_prefetch_explore_delay_time,ig_android_business_insights_qe,ig_android_direct_media_size,ig_android_enable_client_share,ig_android_promoted_posts,ig_android_app_badging_holdout,ig_android_ads_cta_universe,ig_android_mini_inbox_2,ig_android_feed_reshare_button_nux,ig_android_boomerang_feed_attribution,ig_android_fbinvite_qe,ig_fbns_shared,ig_android_direct_full_width_media,ig_android_hscroll_profile_chaining,ig_android_feed_unit_footer,ig_android_media_tighten_space,ig_android_private_follow_request,ig_android_inline_gallery_backoff_hours_universe,ig_android_direct_thread_ui_rewrite,ig_android_rendering_controls,ig_android_ads_full_width_cta_universe,ig_video_max_duration_qe_preuniverse,ig_android_prefetch_explore_expire_time,ig_timestamp_public_test,ig_android_profile,ig_android_dv2_consistent_http_realtime_response,ig_android_enable_share_to_messenger,ig_explore_v3,ig_ranking_following,ig_android_pending_request_search_bar,ig_android_feed_ufi_redesign,ig_android_video_pause_logging_fix,ig_android_default_folder_to_camera,ig_android_video_stitching_7_23,ig_android_profanity_filter,ig_android_business_profile_qe,ig_android_search,ig_android_boomerang_entry,ig_android_inline_gallery_universe,ig_android_ads_overlay_design_universe,ig_android_options_app_invite,ig_android_view_count_decouple_likes_universe,ig_android_periodic_analytics_upload_v2,ig_android_feed_unit_hscroll_auto_advance,ig_peek_profile_photo_universe,ig_android_ads_holdout_universe,ig_android_prefetch_explore,ig_android_direct_bubble_icon,ig_video_use_sve_universe,ig_android_inline_gallery_no_backoff_on_launch_universe,ig_android_image_cache_multi_queue,ig_android_camera_nux,ig_android_immersive_viewer,ig_android_dense_feed_unit_cards,ig_android_sqlite_dev,ig_android_exoplayer,ig_android_add_to_last_post,ig_android_direct_public_threads,ig_android_prefetch_venue_in_composer,ig_android_bigger_share_button,ig_android_dv2_realtime_private_share,ig_android_non_square_first,ig_android_video_interleaved_v2,ig_android_follow_search_bar,ig_android_last_edits,ig_android_video_download_logging,ig_android_ads_loop_count_universe,ig_android_swipeable_filters_blacklist,ig_android_boomerang_layout_white_out_universe,ig_android_ads_carousel_multi_row_universe,ig_android_mentions_invite_v2,ig_android_direct_mention_qe,ig_android_following_follower_social_context' SIG_KEY_VERSION = '4' #username # Instagram username #password # Instagram password #debug # Debug #uuid # UUID #device_id # Device ID #username_id # Username ID #token # _csrftoken #isLoggedIn # Session status #rank_token # Rank token #IGDataPath # Data storage path def __init__(self, username, password, debug = False, IGDataPath = None): m = hashlib.md5() m.update(username.encode('utf-8') + password.encode('utf-8')) self.device_id = self.generateDeviceId(m.hexdigest()) self.setUser(username, password) self.isLoggedIn = False self.LastResponse = None def setUser(self, username, password): self.username = username self.password = password self.uuid = self.generateUUID(True) def login(self, force = False): if (not self.isLoggedIn or force): self.s = requests.Session() # if you need proxy make something like this: # self.s.proxies = {"https" : "http://proxyip:proxyport"} if (self.SendRequest('si/fetch_headers/?challenge_type=signup&guid=' + self.generateUUID(False), None, True)): data = {'phone_id' : self.generateUUID(True), '_csrftoken' : self.LastResponse.cookies['csrftoken'], 'username' : self.username, 'guid' : self.uuid, 'device_id' : self.device_id, 'password' : self.password, 'login_attempt_count' : '0'} if (self.SendRequest('accounts/login/', self.generateSignature(json.dumps(data)), True)): self.isLoggedIn = True self.username_id = self.LastJson["logged_in_user"]["pk"] self.rank_token = "%s_%s" % (self.username_id, self.uuid) self.token = self.LastResponse.cookies["csrftoken"] self.syncFeatures() self.autoCompleteUserList() self.timelineFeed() self.getv2Inbox() self.getRecentActivity() print ("Login success!\n") return True; def syncFeatures(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'id' : self.username_id, '_csrftoken' : self.token, 'experiments' : self.EXPERIMENTS }) return self.SendRequest('qe/sync/', self.generateSignature(data)) def autoCompleteUserList(self): return self.SendRequest('friendships/autocomplete_user_list/') def timelineFeed(self): return self.SendRequest('feed/timeline/') def megaphoneLog(self): return self.SendRequest('megaphone/log/') def expose(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'id' : self.username_id, '_csrftoken' : self.token, 'experiment' : 'ig_android_profile_contextual_feed' }) return self.SendRequest('qe/expose/', self.generateSignature(data)) def logout(self): logout = self.SendRequest('accounts/logout/') def uploadPhoto(self, photo, caption = None, upload_id = None): if upload_id is None: upload_id = str(int(time.time() * 1000)) data = { 'upload_id' : upload_id, '_uuid' : self.uuid, '_csrftoken' : self.token, 'image_compression' : '{"lib_name":"jt","lib_version":"1.3.0","quality":"87"}', 'photo' : ('pending_media_%s.jpg'%upload_id, open(photo, 'rb'), 'application/octet-stream', {'Content-Transfer-Encoding':'binary'}) } m = MultipartEncoder(data, boundary=self.uuid) self.s.headers.update ({'X-IG-Capabilities' : '3Q4=', 'X-IG-Connection-Type' : 'WIFI', 'Cookie2' : '$Version=1', 'Accept-Language' : 'en-US', 'Accept-Encoding' : 'gzip, deflate', 'Content-type': m.content_type, 'Connection' : 'close', 'User-Agent' : self.USER_AGENT}) response = self.s.post(self.API_URL + "upload/photo/", data=m.to_string()) if response.status_code == 200: if self.configure(upload_id, photo, caption): self.expose() return False def uploadVideo(self, video, thumbnail, caption = None, upload_id = None): if upload_id is None: upload_id = str(int(time.time() * 1000)) data = { 'upload_id': upload_id, '_csrftoken': self.token, 'media_type': '2', '_uuid': self.uuid, } m = MultipartEncoder(data, boundary=self.uuid) self.s.headers.update({'X-IG-Capabilities': '3Q4=', 'X-IG-Connection-Type': 'WIFI', 'Host': 'i.instagram.com', 'Cookie2': '$Version=1', 'Accept-Language': 'en-US', 'Accept-Encoding': 'gzip, deflate', 'Content-type': m.content_type, 'Connection': 'keep-alive', 'User-Agent': self.USER_AGENT}) response = self.s.post(self.API_URL + "upload/video/", data=m.to_string()) if response.status_code == 200: body = json.loads(response.text) upload_url = body['video_upload_urls'][3]['url'] upload_job = body['video_upload_urls'][3]['job'] videoData = open(video, 'rb').read() #solve issue #85 TypeError: slice indices must be integers or None or have an __index__ method request_size = int(math.floor(len(videoData) / 4)) lastRequestExtra = (len(videoData) - (request_size * 3)) headers = copy.deepcopy(self.s.headers) self.s.headers.update({'X-IG-Capabilities': '3Q4=', 'X-IG-Connection-Type': 'WIFI', 'Cookie2': '$Version=1', 'Accept-Language': 'en-US', 'Accept-Encoding': 'gzip, deflate', 'Content-type': 'application/octet-stream', 'Session-ID': upload_id, 'Connection': 'keep-alive', 'Content-Disposition': 'attachment; filename="video.mov"', 'job': upload_job, 'Host': 'upload.instagram.com', 'User-Agent': self.USER_AGENT}) for i in range(0, 4): start = i * request_size if i == 3: end = i * request_size + lastRequestExtra else: end = (i + 1) * request_size length = lastRequestExtra if i == 3 else request_size content_range = "bytes {start}-{end}/{lenVideo}".format(start=start, end=(end - 1), lenVideo=len(videoData)).encode('utf-8') self.s.headers.update({'Content-Length': str(end - start), 'Content-Range': content_range, }) response = self.s.post(upload_url, data=videoData[start:start + length]) self.s.headers = headers if response.status_code == 200: if self.configureVideo(upload_id, video, thumbnail, caption): self.expose() return False def direct_share(self, media_id, recipients, text = None): if type(recipients) != type([]): recipients = [str(recipients)] recipient_users = '"",""'.join(str(r) for r in recipients) endpoint = 'direct_v2/threads/broadcast/media_share/?media_type=photo' boundary = self.uuid bodies = [ { 'type' : 'form-data', 'name' : 'media_id', 'data' : media_id, }, { 'type' : 'form-data', 'name' : 'recipient_users', 'data' : '[["{}"]]'.format(recipient_users), }, { 'type' : 'form-data', 'name' : 'client_context', 'data' : self.uuid, }, { 'type' : 'form-data', 'name' : 'thread_ids', 'data' : '["0"]', }, { 'type' : 'form-data', 'name' : 'text', 'data' : text or '', }, ] data = self.buildBody(bodies,boundary) self.s.headers.update ( { 'User-Agent' : self.USER_AGENT, 'Proxy-Connection' : 'keep-alive', 'Connection': 'keep-alive', 'Accept': '*/*', 'Content-Type': 'multipart/form-data; boundary={}'.format(boundary), 'Accept-Language': 'en-en', } ) #self.SendRequest(endpoint,post=data) #overwrites 'Content-type' header and boundary is missed response = self.s.post(self.API_URL + endpoint, data=data) if response.status_code == 200: self.LastResponse = response self.LastJson = json.loads(response.text) return True else: print ("Request return " + str(response.status_code) + " error!") # for debugging try: self.LastResponse = response self.LastJson = json.loads(response.text) except: pass return False def configureVideo(self, upload_id, video, thumbnail, caption = ''): clip = VideoFileClip(video) self.uploadPhoto(photo=thumbnail, caption=caption, upload_id=upload_id) data = json.dumps({ 'upload_id': upload_id, 'source_type': 3, 'poster_frame_index': 0, 'length': 0.00, 'audio_muted': False, 'filter_type': 0, 'video_result': 'deprecated', 'clips': { 'length': clip.duration, 'source_type': '3', 'camera_position': 'back', }, 'extra': { 'source_width': clip.size[0], 'source_height': clip.size[1], }, 'device': self.DEVICE_SETTINTS, '_csrftoken': self.token, '_uuid': self.uuid, '_uid': self.username_id, 'caption': caption, }) return self.SendRequest('media/configure/?video=1', self.generateSignature(data)) def configure(self, upload_id, photo, caption = ''): (w,h) = getImageSize(photo) data = json.dumps({ '_csrftoken' : self.token, 'media_folder' : 'Instagram', 'source_type' : 4, '_uid' : self.username_id, '_uuid' : self.uuid, 'caption' : caption, 'upload_id' : upload_id, 'device' : self.DEVICE_SETTINTS, 'edits' : { 'crop_original_size': [w * 1.0, h * 1.0], 'crop_center' : [0.0, 0.0], 'crop_zoom' : 1.0 }, 'extra' : { 'source_width' : w, 'source_height' : h, }}) return self.SendRequest('media/configure/?', self.generateSignature(data)) def editMedia(self, mediaId, captionText = ''): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'caption_text' : captionText }) return self.SendRequest('media/'+ str(mediaId) +'/edit_media/', self.generateSignature(data)) def removeSelftag(self, mediaId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('media/'+ str(mediaId) +'/remove/', self.generateSignature(data)) def mediaInfo(self, mediaId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'media_id' : mediaId }) return self.SendRequest('media/'+ str(mediaId) +'/info/', self.generateSignature(data)) def deleteMedia(self, mediaId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'media_id' : mediaId }) return self.SendRequest('media/'+ str(mediaId) +'/delete/', self.generateSignature(data)) def changePassword(self, newPassword): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'old_password' : self.password, 'new_password1' : newPassword, 'new_password2' : newPassword }) return self.SendRequest('accounts/change_password/', self.generateSignature(data)) def explore(self): return self.SendRequest('discover/explore/') def comment(self, mediaId, commentText): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'comment_text' : commentText }) return self.SendRequest('media/'+ str(mediaId) +'/comment/', self.generateSignature(data)) def deleteComment(self, mediaId, commentId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('media/'+ str(mediaId) +'/comment/'+ str(commentId) +'/delete/', self.generateSignature(data)) def changeProfilePicture(self, photo): # TODO Instagram.php 705-775 return False def removeProfilePicture(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('accounts/remove_profile_picture/', self.generateSignature(data)) def setPrivateAccount(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('accounts/set_private/', self.generateSignature(data)) def setPublicAccount(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('accounts/set_public/', self.generateSignature(data)) def getProfileData(self): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token }) return self.SendRequest('accounts/current_user/?edit=true', self.generateSignature(data)) def editProfile(self, url, phone, first_name, biography, email, gender): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'external_url' : url, 'phone_number' : phone, 'username' : self.username, 'full_name' : first_name, 'biography' : biography, 'email' : email, 'gender' : gender, }) return self.SendRequest('accounts/edit_profile/', self.generateSignature(data)) def getUsernameInfo(self, usernameId): return self.SendRequest('users/'+ str(usernameId) +'/info/') def getSelfUsernameInfo(self): return self.getUsernameInfo(self.username_id) def getRecentActivity(self): activity = self.SendRequest('news/inbox/?') return activity def getFollowingRecentActivity(self): activity = self.SendRequest('news/?') return activity def getv2Inbox(self): inbox = self.SendRequest('direct_v2/inbox/?') return inbox def getUserTags(self, usernameId): tags = self.SendRequest('usertags/'+ str(usernameId) +'/feed/?rank_token='+ str(self.rank_token) +'&ranked_content=true&') return tags def getSelfUserTags(self): return self.getUserTags(self.username_id) def tagFeed(self, tag): userFeed = self.SendRequest('feed/tag/'+ str(tag) +'/?rank_token=' + str(self.rank_token) + '&ranked_content=true&') return userFeed def getMediaLikers(self, mediaId): likers = self.SendRequest('media/'+ str(mediaId) +'/likers/?') return likers def getGeoMedia(self, usernameId): locations = self.SendRequest('maps/user/'+ str(usernameId) +'/') return locations def getSelfGeoMedia(self): return self.getGeoMedia(self.username_id) def fbUserSearch(self, query): query = self.SendRequest('fbsearch/topsearch/?context=blended&query='+ str(query) +'&rank_token='+ str(self.rank_token)) return query def searchUsers(self, query): query = self.SendRequest('users/search/?ig_sig_key_version='+ str(self.SIG_KEY_VERSION) +'&is_typeahead=true&query='+ str(query) +'&rank_token='+ str(self.rank_token)) return query def searchUsername(self, usernameName): query = self.SendRequest('users/'+ str(usernameName) +'/usernameinfo/') return query def syncFromAdressBook(self, contacts): return self.SendRequest('address_book/link/?include=extra_display_name,thumbnails', "contacts=" + json.dumps(contacts)) def searchTags(self, query): query = self.SendRequest('tags/search/?is_typeahead=true&q='+ str(query) +'&rank_token='+ str(self.rank_token)) return query def getTimeline(self): query = self.SendRequest('feed/timeline/?rank_token='+ str(self.rank_token) +'&ranked_content=true&') return query def getUserFeed(self, usernameId, maxid = '', minTimestamp = None): query = self.SendRequest('feed/user/' + str(usernameId) + '/?max_id=' + str(maxid) + '&min_timestamp=' + str(minTimestamp) + '&rank_token='+ str(self.rank_token) +'&ranked_content=true') return query def getSelfUserFeed(self, maxid = '', minTimestamp = None): return self.getUserFeed(self.username_id, maxid, minTimestamp) def getHashtagFeed(self, hashtagString, maxid = ''): return self.SendRequest('feed/tag/'+hashtagString+'/?max_id='+str(maxid)+'&rank_token='+self.rank_token+'&ranked_content=true&') def searchLocation(self, query): locationFeed = self.SendRequest('fbsearch/places/?rank_token='+ str(self.rank_token) +'&query=' + str(query)) return locationFeed def getLocationFeed(self, locationId, maxid = ''): return self.SendRequest('feed/location/'+str(locationId)+'/?max_id='+maxid+'&rank_token='+self.rank_token+'&ranked_content=true&') def getPopularFeed(self): popularFeed = self.SendRequest('feed/popular/?people_teaser_supported=1&rank_token='+ str(self.rank_token) +'&ranked_content=true&') return popularFeed def getUserFollowings(self, usernameId, maxid = ''): return self.SendRequest('friendships/'+ str(usernameId) +'/following/?max_id='+ str(maxid) +'&ig_sig_key_version='+ self.SIG_KEY_VERSION +'&rank_token='+ self.rank_token) def getSelfUsersFollowing(self): return self.getUserFollowings(self.username_id) def getUserFollowers(self, usernameId, maxid = ''): if maxid == '': return self.SendRequest('friendships/'+ str(usernameId) +'/followers/?rank_token='+ self.rank_token) else: return self.SendRequest('friendships/'+ str(usernameId) +'/followers/?rank_token='+ self.rank_token + '&max_id='+ str(maxid)) def getSelfUserFollowers(self): return self.getUserFollowers(self.username_id) def like(self, mediaId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'media_id' : mediaId }) return self.SendRequest('media/'+ str(mediaId) +'/like/', self.generateSignature(data)) def unlike(self, mediaId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, '_csrftoken' : self.token, 'media_id' : mediaId }) return self.SendRequest('media/'+ str(mediaId) +'/unlike/', self.generateSignature(data)) def getMediaComments(self, mediaId, max_id=''): return self.SendRequest('media/'+ mediaId +'/comments/?max_id='+max_id) def setNameAndPhone(self, name = '', phone = ''): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'first_name' : name, 'phone_number' : phone, '_csrftoken' : self.token }) return self.SendRequest('accounts/set_phone_and_name/', self.generateSignature(data)) def getDirectShare(self): return self.SendRequest('direct_share/inbox/?') def backup(self): # TODO Instagram.php 1470-1485 return False def follow(self, userId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'user_id' : userId, '_csrftoken' : self.token }) return self.SendRequest('friendships/create/'+ str(userId) +'/', self.generateSignature(data)) def unfollow(self, userId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'user_id' : userId, '_csrftoken' : self.token }) return self.SendRequest('friendships/destroy/'+ str(userId) +'/', self.generateSignature(data)) def block(self, userId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'user_id' : userId, '_csrftoken' : self.token }) return self.SendRequest('friendships/block/'+ str(userId) +'/', self.generateSignature(data)) def unblock(self, userId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'user_id' : userId, '_csrftoken' : self.token }) return self.SendRequest('friendships/unblock/'+ str(userId) +'/', self.generateSignature(data)) def userFriendship(self, userId): data = json.dumps({ '_uuid' : self.uuid, '_uid' : self.username_id, 'user_id' : userId, '_csrftoken' : self.token }) return self.SendRequest('friendships/show/'+ str(userId) +'/', self.generateSignature(data)) def getLikedMedia(self,maxid=''): return self.SendRequest('feed/liked/?max_id='+str(maxid)) def generateSignature(self, data): try: parsedData = urllib.parse.quote(data) except AttributeError: parsedData = urllib.quote(data) return 'ig_sig_key_version=' + self.SIG_KEY_VERSION + '&signed_body=' + hmac.new(self.IG_SIG_KEY.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest() + '.' + parsedData def generateDeviceId(self, seed): volatile_seed = "12345" m = hashlib.md5() m.update(seed.encode('utf-8') + volatile_seed.encode('utf-8')) return 'android-' + m.hexdigest()[:16] def generateUUID(self, type): #according to https://github.com/LevPasha/Instagram-API-python/pull/16/files#r77118894 #uuid = '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' % (random.randint(0, 0xffff), # random.randint(0, 0xffff), random.randint(0, 0xffff), # random.randint(0, 0x0fff) | 0x4000, # random.randint(0, 0x3fff) | 0x8000, # random.randint(0, 0xffff), random.randint(0, 0xffff), # random.randint(0, 0xffff)) generated_uuid = str(uuid.uuid4()) if (type): return generated_uuid else: return generated_uuid.replace('-', '') def generateUploadId(): return str(calendar.timegm(datetime.utcnow().utctimetuple())) def buildBody(self,bodies, boundary): body = u'' for b in bodies: body += u'--{boundary}\r\n'.format(boundary=boundary) body += u'Content-Disposition: {b_type}; name="{b_name}"'.format(b_type = b['type'], b_name = b['name']) _filename = b.get('filename',None) _headers = b.get('headers',None) if _filename: _filename, ext = os.path.splitext(_filename) _body += u'; filename="pending_media_{uid}.{ext}"'.format(uid = self.generateUploadId(), ext = ext) if _headers and type(_headers) == type([]): for h in _headers: _body += u'\r\n{header}'.format(header = h) body += u'\r\n\r\n{data}\r\n'.format(data = b['data']) body += u'--{boundary}--'.format(boundary = boundary) return body; def SendRequest(self, endpoint, post = None, login = False): if (not self.isLoggedIn and not login): raise Exception("Not logged in!\n") return; self.s.headers.update ({'Connection' : 'close', 'Accept' : '*/*', 'Content-type' : 'application/x-www-form-urlencoded; charset=UTF-8', 'Cookie2' : '$Version=1', 'Accept-Language' : 'en-US', 'User-Agent' : self.USER_AGENT}) if (post != None): # POST response = self.s.post(self.API_URL + endpoint, data=post) # , verify=False else: # GET response = self.s.get(self.API_URL + endpoint) # , verify=False if response.status_code == 200: self.LastResponse = response self.LastJson = json.loads(response.text) return True else: print ("Request return " + str(response.status_code) + " error!") # for debugging try: self.LastResponse = response self.LastJson = json.loads(response.text) except: pass return False def getTotalFollowers(self,usernameId): followers = [] next_max_id = '' while 1: self.getUserFollowers(usernameId,next_max_id) temp = self.LastJson for item in temp["users"]: followers.append(item) if temp["big_list"] == False: return followers next_max_id = temp["next_max_id"] def getTotalFollowings(self,usernameId): followers = [] next_max_id = '' while 1: self.getUserFollowings(usernameId,next_max_id) temp = self.LastJson for item in temp["users"]: followers.append(item) if temp["big_list"] == False: return followers next_max_id = temp["next_max_id"] def getTotalUserFeed(self, usernameId, minTimestamp = None): user_feed = [] next_max_id = '' while 1: self.getUserFeed(usernameId, next_max_id, minTimestamp) temp = self.LastJson for item in temp["items"]: user_feed.append(item) if temp["more_available"] == False: return user_feed next_max_id = temp["next_max_id"] def getTotalSelfUserFeed(self, minTimestamp = None): return self.getTotalUserFeed(self.username_id, minTimestamp) def getTotalSelfFollowers(self): return self.getTotalFollowers(self.username_id) def getTotalSelfFollowings(self): return self.getTotalFollowings(self.username_id) def getTotalLikedMedia(self,scan_rate = 1): next_id = '' liked_items = [] for x in range(0,scan_rate): temp = self.getLikedMedia(next_id) temp = self.LastJson next_id = temp["next_max_id"] for item in temp["items"]: liked_items.append(item) return liked_items