diff --git a/.img/banner.png b/.img/banner.png new file mode 100644 index 00000000..e915a9a7 Binary files /dev/null and b/.img/banner.png differ diff --git a/README.md b/README.md index 9172cf49..ef744130 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![](https://img.shields.io/badge/version-0.7-green)](https://github.com/Datalux/Osintgram/releases/tag/0.7) +[![](https://img.shields.io/badge/version-0.8-green)](https://github.com/Datalux/Osintgram/releases/tag/0.8) [![](https://img.shields.io/badge/license-GPLv3-blue)](https://img.shields.io/badge/license-GPLv3-blue) [![](https://img.shields.io/badge/language-Python3-red)](https://img.shields.io/badge/language-Python3-red) @@ -27,15 +27,16 @@ Osintgram offers an interactive shell to perform analysis on Instagram account o - tagged Get list of users tagged by target - target Set new target - wcommented Get a list of user who commented target's photos +- wtagged Get a list of user who tagged target ``` -You can find detailed commands usage [here](commands.md). +You can find detailed commands usage [here](doc/COMMANDS.md). -[**Latest version**](https://github.com/Datalux/Osintgram/releases/tag/0.7) | -[CHANGELOG](CHANGELOG.md) +[**Latest version**](https://github.com/Datalux/Osintgram/releases/tag/0.8) | +[CHANGELOG](doc/CHANGELOG.md) ## Tools

- +

@@ -72,4 +73,4 @@ Run `git pull` in Osintgram directory You can propose a feature request opening an issue or a pull request. ## External library -Instagram API: https://github.com/ping/instagram_private_api \ No newline at end of file +Instagram API: https://gDOCithub.com/ping/instagram_private_api diff --git a/cmd.png b/cmd.png deleted file mode 100644 index d6c46e58..00000000 Binary files a/cmd.png and /dev/null differ diff --git a/CHANGELOG.md b/doc/CHANGELOG.md similarity index 82% rename from CHANGELOG.md rename to doc/CHANGELOG.md index 11e9c9e4..fb0cfff4 100644 --- a/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [0.8](https://github.com/Datalux/Osintgram/releases/tag/0.8) + +**Enhancements** +- Added `wtagged` command (#38) +- Access private profiles if you following targets (#37) +- Added more info in `info` command (#36) + +**Bug fixes** +- Minor bug fix in `addrs` commands (9b9086a) + + ## [0.7](https://github.com/Datalux/Osintgram/releases/tag/0.7) **Enhancements** diff --git a/commands.md b/doc/COMMANDS.md similarity index 92% rename from commands.md rename to doc/COMMANDS.md index 48d54c75..2ca46eba 100644 --- a/commands.md +++ b/doc/COMMANDS.md @@ -15,6 +15,7 @@ - stories Download user's stories - tagged Get list of users tagged by target - wcommented Get a list of user who commented target's photos +- wtagged Get a list of user who tagged target ``` ### addrs @@ -56,6 +57,9 @@ Show target info like: - is business account? - business catagory (if target has business account) - is verified? +- business email (if available) +- HD profile picture url +- connected Facebook page (if available) ### JSON Can set preference to export commands output as JSON in output folder. It save output in `_.JSON` file. @@ -97,5 +101,7 @@ Return a list of users tagged by target with ID, username and full name ## wcommented Return a list of users who commented target's photos sorted by number of comments +## wtagged +Return a list of users who tagged target sorted by number of photos diff --git a/doc/doc.pdf b/doc/doc.pdf deleted file mode 100644 index 5c99b724..00000000 Binary files a/doc/doc.pdf and /dev/null differ diff --git a/main.py b/main.py index 942c1888..73b7d50b 100644 --- a/main.py +++ b/main.py @@ -16,7 +16,7 @@ def printlogo(): pc.printout("\_______ /____ >__|___| /__| \___ /|__| (____ /__|_| /\n", pc.YELLOW) pc.printout(" \/ \/ \/ /_____/ \/ \/ \n", pc.YELLOW) print('\n') - pc.printout("Version 0.7 - Developed by Giuseppe Criscione - 2019\n\n", pc.YELLOW) + pc.printout("Version 0.8 - Developed by Giuseppe Criscione - 2019\n\n", pc.YELLOW) pc.printout("Type 'list' to show all allowed commands\n") pc.printout("Type 'FILE=y' to save results to files like '_.txt (deafult is disabled)'\n") pc.printout("Type 'FILE=n' to disable saving to files'\n") @@ -40,6 +40,8 @@ def cmdlist(): print("Get target followers") pc.printout("followings\t") print("Get users followed by target") + pc.printout("fwersemail\t") + print("Get email of users followed by target") pc.printout("hashtags\t") print("Get hashtags used by target") pc.printout("info\t\t") @@ -62,6 +64,8 @@ def cmdlist(): print("Set new target") pc.printout("wcommented\t") print("Get a list of user who commented target's photos") + pc.printout("wtagged\t\t") + print("Get a list of user who tagged target") def signal_handler(sig, frame): @@ -102,6 +106,8 @@ def signal_handler(sig, frame): api.get_followers() elif cmd == "followings": api.get_followings() + elif cmd == 'fwersemail': + api.get_fwersemail() elif cmd == "hashtags": api.get_hashtags() elif cmd == "info": @@ -124,6 +130,8 @@ def signal_handler(sig, frame): api.change_target() elif cmd == "wcommented": api.get_people_who_commented() + elif cmd == "wtagged": + api.get_people_who_tagged() elif cmd == "FILE=y": api.set_write_file(True) elif cmd == "FILE=n": diff --git a/src/Osintgram.py b/src/Osintgram.py index bc8a17ca..a870dcd6 100644 --- a/src/Osintgram.py +++ b/src/Osintgram.py @@ -21,6 +21,7 @@ class Osintgram: user_id = None target_id = None is_private = True + following = False target = "" writeFile = False jsonDump = False @@ -39,6 +40,7 @@ def setTarget(self, target): user = self.get_user(target) self.target_id = user['id'] self.is_private = user['is_private'] + self.following = self.check_following() self.__printTargetBanner__() def __getUsername__(self): @@ -94,9 +96,15 @@ def __printTargetBanner__(self): pc.printout(self.api.username, pc.CYAN) pc.printout(". Target: ", pc.GREEN) pc.printout(str(self.target), pc.CYAN) - pc.printout(" [" + str(self.target_id) + "] ") + pc.printout(" [" + str(self.target_id) + "]") if self.is_private: - pc.printout("[PRIVATE PROFILE]", pc.RED) + pc.printout(" [PRIVATE PROFILE]", pc.BLUE) + if self.following: + pc.printout(" [FOLLOWING]", pc.GREEN) + else: + pc.printout(" [NOT FOLLOWING]", pc.RED) + + print('\n') def change_target(self): @@ -106,8 +114,7 @@ def change_target(self): return def get_addrs(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target localizations...\n") @@ -117,7 +124,7 @@ def get_addrs(self): locations = {} for post in data: - if post['location'] is not None: + if 'location' in post and post['location'] is not None: lat = post['location']['lat'] lng = post['location']['lng'] locations[str(lat) + ', ' + str(lng)] = post.get('taken_at') @@ -173,8 +180,7 @@ def get_addrs(self): pc.printout("Sorry! No results found :-(\n", pc.RED) def get_captions(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target captions...\n") @@ -232,8 +238,7 @@ def get_captions(self): return def get_total_comments(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target total comments...\n") @@ -266,8 +271,7 @@ def get_total_comments(self): pc.printout(" comments in " + str(posts) + " posts\n") def get_followers(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target followers...\n") @@ -319,8 +323,7 @@ def get_followers(self): print(t) def get_followings(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target followings...\n") @@ -372,8 +375,7 @@ def get_followings(self): print(t) def get_hashtags(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target hashtags...\n") @@ -449,9 +451,9 @@ def get_user_info(self): pc.printout(str(data['full_name']) + '\n') pc.printout("[BIOGRAPHY] ", pc.CYAN) pc.printout(str(data['biography']) + '\n') - pc.printout("[FOLLOWED] ", pc.GREEN) + pc.printout("[FOLLOWED] ", pc.BLUE) pc.printout(str(data['edge_followed_by']['count']) + '\n') - pc.printout("[FOLLOW] ", pc.BLUE) + pc.printout("[FOLLOW] ", pc.GREEN) pc.printout(str(data['edge_follow']['count']) + '\n') pc.printout("[BUSINESS ACCOUNT] ", pc.RED) pc.printout(str(data['is_business_account']) + '\n') @@ -460,6 +462,14 @@ def get_user_info(self): pc.printout(str(data['business_category_name']) + '\n') pc.printout("[VERIFIED ACCOUNT] ", pc.CYAN) pc.printout(str(data['is_verified']) + '\n') + if data['business_email']: + pc.printout("[BUSINESS EMAIL] ", pc.BLUE) + pc.printout(str(data['business_email']) + '\n') + pc.printout("[HD PROFILE PIC] ", pc.GREEN) + pc.printout(str(data['profile_pic_url_hd']) + '\n') + if data['connected_fb_page']: + pc.printout("[FB PAGE] ", pc.RED) + pc.printout(str(data['business_email']) + '\n') if self.jsonDump: user = { @@ -469,8 +479,14 @@ def get_user_info(self): 'edge_followed_by': data['edge_followed_by']['count'], 'edge_follow': data['edge_follow']['count'], 'is_business_account': data['is_business_account'], - 'is_verified': data['is_verified'] + 'is_verified': data['is_verified'], + 'profile_pic_url_hd': data['profile_pic_url_hd'] } + if data['business_email']: + user['business_email'] = data['business_email'] + if data['connected_fb_page']: + user['connected_fb_page'] = data['connected_fb_page'] + json_file_name = "output/" + self.target + "_info.json" with open(json_file_name, 'w') as f: json.dump(user, f) @@ -481,8 +497,7 @@ def get_user_info(self): sys.exit(2) def get_total_likes(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target total likes...\n") @@ -515,8 +530,7 @@ def get_total_likes(self): pc.printout(" likes in " + str(posts) + " posts\n") def get_media_type(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target captions...\n") @@ -564,8 +578,7 @@ def get_media_type(self): pc.printout("Sorry! No results found :-(\n", pc.RED) def get_people_who_commented(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for users who commented...\n") @@ -622,9 +635,76 @@ def get_people_who_commented(self): else: pc.printout("Sorry! No results found :-(\n", pc.RED) + def get_people_who_tagged(self): + if self.check_private_profile(): + return + + pc.printout("Searching for users who tagged target...\n") + + posts = [] + + result = self.api.usertag_feed(self.target_id) + posts.extend(result.get('items', [])) + + next_max_id = result.get('next_max_id') + while next_max_id: + results = self.api.user_feed(str(self.target_id), max_id=next_max_id) + posts.extend(results.get('items', [])) + next_max_id = results.get('next_max_id') + + if len(posts) > 0: + pc.printout("\nWoohoo! We found " + str(len(posts)) + " photos\n", pc.GREEN) + + users = [] + + for post in posts: + if not any(u['id'] == post['user']['pk'] for u in users): + user = { + 'id': post['user']['pk'], + 'username': post['user']['username'], + 'full_name': post['user']['full_name'], + 'counter': 1 + } + users.append(user) + else: + for user in users: + if user['id'] == post['user']['pk']: + user['counter'] += 1 + break + + ssort = sorted(users, key=lambda value: value['counter'], reverse=True) + + json_data = {} + + t = PrettyTable() + + t.field_names = ['Photos', 'ID', 'Username', 'Full Name'] + t.align["Photos"] = "l" + t.align["ID"] = "l" + t.align["Username"] = "l" + t.align["Full Name"] = "l" + + for u in ssort: + t.add_row([str(u['counter']), u['id'], u['username'], u['full_name']]) + + print(t) + + if self.writeFile: + file_name = "output/" + self.target + "_users_who_tagged.txt" + file = open(file_name, "w") + file.write(str(t)) + file.close() + + if self.jsonDump: + json_data['users_who_tagged'] = ssort + json_file_name = "output/" + self.target + "_users_who_tagged.json" + with open(json_file_name, 'w') as f: + json.dump(json_data, f) + else: + pc.printout("Sorry! No results found :-(\n", pc.RED) + def get_photo_description(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return content = urllib.request.urlopen("https://www.instagram.com/" + str(self.target) + "/?__a=1") @@ -673,8 +753,7 @@ def get_photo_description(self): pc.printout("Sorry! No results found :-(\n", pc.RED) def get_user_photo(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return limit = -1 @@ -764,8 +843,7 @@ def get_user_propic(self): sys.exit(2) def get_user_stories(self): - if self.is_private: - pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + if self.check_private_profile(): return pc.printout("Searching for target stories...\n") @@ -968,3 +1046,79 @@ def onlogin_callback(self, api, new_settings_file): with open(new_settings_file, 'w') as outfile: json.dump(cache_settings, outfile, default=self.to_json) #print('SAVED: {0!s}'.format(new_settings_file)) + + def check_following(self): + endpoint = 'users/{user_id!s}/full_detail_info/'.format(**{'user_id': self.target_id}) + return self.api._call_api(endpoint)['user_detail']['user']['friendship_status']['following'] + + def check_private_profile(self): + if self.is_private and not self.following: + pc.printout("Impossible to execute command: user has private profile\n", pc.RED) + return True + return False + + def get_fwersemail(self): + if self.check_private_profile(): + return + + pc.printout("Searching for emails of target followers... this can take a few minutes\n") + + followers = [] + + rank_token = AppClient.generate_uuid() + data = self.api.user_followers(str(self.target_id), rank_token=rank_token) + + for user in data['users']: + u = { + 'id': user['pk'], + 'username': user['username'], + 'full_name': user['full_name'] + } + followers.append(u) + + results = [] + + for follow in followers: + req = urllib.request.urlopen("https://www.instagram.com/" + str(follow['username']) + "/?__a=1") + data = json.load(req)['graphql']['user'] + if data['business_email']: + follow['email'] = data['business_email'] + results.append(follow) + + if len(results) > 0: + + t = PrettyTable(['ID', 'Username', 'Full Name', 'Email']) + t.align["ID"] = "l" + t.align["Username"] = "l" + t.align["Full Name"] = "l" + t.align["Email"] = "l" + + json_data = {} + + for node in results: + t.add_row([str(node['id']), node['username'], node['full_name'], node['email']]) + + if self.writeFile: + file_name = "output/" + self.target + "_followers.txt" + file = open(file_name, "w") + file.write(str(t)) + file.close() + + if self.jsonDump: + json_data['followers'] = results + json_file_name = "output/" + self.target + "_followers.json" + with open(json_file_name, 'w') as f: + json.dump(json_data, f) + + print(t) + else: + pc.printout("Sorry! No results found :-(\n", pc.RED) + + + + + + + + +