diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..743afd1 --- /dev/null +++ b/src/main.py @@ -0,0 +1,445 @@ +import os +import logging +import platform +import subprocess +import urllib.request +from pprint import pprint +from lib import colors,banner +from datetime import datetime +try: + import requests +except ImportError: + print(f'{colors.white}[{colors.green}*{colors.white}] Installing requirement(s). Please wait...{colors.reset}') + subprocess.run(['pip', 'install', 'requests'],shell=False) + + +class octosuite: + def __init__(self): + # Path attribute + self.path_attrs =['size','type','path','sha','html_url'] + # Path attribute dictionary + self.path_attr_dict = {'size': 'Size (bytes)', + 'type': 'Type', + 'path': 'Path', + 'sha': 'SHA', + 'html_url': 'URL'} + + # Organization attributes + self.org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at'] + # Organization attribute dictionary + self.org_attr_dict = {'avatar_url': 'Profile Photo', + 'login': 'Username', + 'id': 'ID#', + 'node_id': 'Node ID', + 'email': 'Email', + 'description': 'About', + 'location': 'Location', + 'blog': 'Blog', + 'followers': 'Followers', + 'following': 'Following', + 'twitter_username': 'Twitter Handle', + 'public_gists': 'Gists (public)', + 'public_repos': 'Repositories (public)', + 'type': 'Account type', + 'is_verified': 'Is verified?', + 'has_organization_projects': 'Has organization projects?', + 'has_repository_projects': 'Has repository projects?', + 'created_at': 'Created at', + 'updated_at': 'Updated at'} + # Repository attributes + self.repo_attrs = ['id','description','forks','allow_forking','fork','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at'] + # Repository attribute dictionary + self.repo_attr_dict = {'id': 'ID#', + 'description': 'About', + 'forks': 'Forks', + 'allow_forking': 'Is forkable?', + 'fork': 'Is fork?', + 'stargazers_count': 'Stars', + 'watchers': 'Watchers', + 'license': 'License', + 'default_branch': 'Branch', + 'visibility': 'Visibility', + 'language': 'Language(s)', + 'open_issues': 'Open issues', + 'topics': 'Topics', + 'homepage': 'Homepage', + 'clone_url': 'Clone URL', + 'ssh_url': 'SSH URL', + 'private': 'Is private?', + 'archived': 'Is archived?', + 'is_template': 'Is template?', + 'has_wiki': 'Has wiki?', + 'has_pages': 'Has pages?', + 'has_projects': 'Has projects?', + 'has_issues': 'Has issues?', + 'has_downloads': 'Has downloads?', + 'pushed_at': 'Pushed at', + 'created_at': 'Created at', + 'updated_at': 'Updated at'} + + # Profile attributes + self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at'] + # Profile attribute dictionary + self.profile_attr_dict = {'avatar_url': 'Profile Photo', + 'login': 'Username', + 'id': 'ID#', + 'node_id': 'Node ID', + 'bio': 'Bio', + 'blog': 'Blog', + 'location': 'Location', + 'followers': 'Followers', + 'following': 'Following', + 'twitter_username': 'Twitter Handle', + 'public_gists': 'Gists (public)', + 'public_repos': 'Repositories (public)', + 'company': 'Organization', + 'hireable': 'Is hireable?', + 'site_admin': 'Is site admin?', + 'created_at': 'Joined at', + 'updated_at': 'Updated at'} + + # User attributes + self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url'] + # User attribute dictionary + self.user_attr_dict = {'avatar_url': 'Profile Photo', + 'id': 'ID#', + 'node_id': 'Node ID', + 'gravatar_id': 'Gravatar ID', + 'site_admin': 'Is site admin?', + 'type': 'Account type', + 'html_url': 'URL'} + + # Topic atrributes + self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at'] + # Topic attribute dictionary + self.topic_attr_dict = {'score': 'Score', + 'curated': 'Curated', + 'featured': 'Featured', + 'display_name': 'Display Name', + 'created_by': 'Created by', + 'created_at': 'Created at', + 'updated_at': 'Updated at'} + + # Gists attributes + self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at'] + # Gists attribute dictionary + self.gists_attr_dict = {'node_id': 'Node ID', + 'description': 'About', + 'comments': 'Comments', + 'files': 'Files', + 'git_push_url': 'Git Push URL', + 'public': 'Is public?', + 'truncated': 'Is truncated?', + 'updated_at': 'Updated at'} + + # Issue attributes + self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body'] + # Issue attribute dict + self.issue_attr_dict = {'id': 'ID#', + 'node_id': 'Node ID', + 'score': 'Score', + 'state': 'State', + 'closed_at': 'Closed at', + 'number': 'Number', + 'comments': 'Comments', + 'milestone': 'Milestone', + 'assignee': 'Assignee', + 'assignees': 'Assignees', + 'labels': 'Labels', + 'draft': 'Is draft?', + 'locked': 'Is locked?', + 'created_at': 'Created at', + 'body': 'Body'} + + # Author dictionary + self.author_dict = {'Alias': 'rly0nheart', + 'Country': 'Zambia, Africa', + 'Github': 'https://github.com/rly0nheart', + 'Twitter': 'https://twitter.com/rly0nheart', + 'Facebook': 'https://fb.me/rly0nheart', + 'About.me': 'https://about.me/rly0nheart'} + + def on_start(self): + logging.info(f'Started new session on {platform.node()}') + while True: + if platform.system() == 'Windows': + subprocess.run(['cls']) + else: + subprocess.run(['clear'],shell=False) + + print(banner.banner) + command = input(f'''{colors.white}┌─({colors.red}{platform.node()}{colors.white}@{colors.red}octosuite{colors.white})-[{colors.green}{os.getcwd()}{colors.white}]\n└─╼[{colors.green}:~{colors.white}]{colors.reset} ''') + if command == 'orginfo': + self.org_info() + elif command == 'userinfo': + self.user_profile() + elif command == 'repoinfo': + self.repo_info() + elif command == 'pathcontents': + self.path_contents() + elif command == 'orgrepos': + self.org_repos() + elif command == 'userrepos': + self.user_repos() + elif command == 'usergists': + self.user_gists() + elif command == 'userfollowers': + self.followers() + elif command == 'userfollowing': + self.following() + elif command == 'usersearch': + self.user_search() + elif command == 'reposearch': + self.repo_search() + elif command == 'topicsearch': + self.topic_search() + elif command == 'issuesearch': + self.issue_search() + elif command == 'commitsearch': + self.commits_search() + elif command == 'update': + self.update() + elif command == 'author': + self.author() + elif command == 'help': + print(self.help()) + elif command == 'exit': + logging.info('Session terminated.') + exit(f'\n{colors.white}[{colors.red}-{colors.white}] Session terminated.{colors.reset}') + else: + print(f'\n{colors.white}[{colors.red}!{colors.white}] Unknown command: ‘{command}’{colors.reset}') + logging.warning(f'Unknown command: ‘{command}’') + + input(f'\n{colors.white}^ Press any key to continue{colors.reset} ') + + + def org_info(self): + organization = input(f'{colors.white}@{colors.green}Organization{colors.white} >> {colors.reset}') + api = f'https://api.github.com/orgs/{organization}' + response = requests.get(api).json() + print(f"\n{colors.white}{response['name']}{colors.reset}") + for attr in self.org_attrs: + print(f'{colors.white}├─ {self.org_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}') + + + # Fetching user information + def user_profile(self): + username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') + api = f'https://api.github.com/users/{username}' + response = requests.get(api) + if response.status_code != 200: + print(f'{colors.white}[{colors.red}-{colors.white}] @{username} {colors.red}Not found{colors.reset}') + + response = response.json() + print(f"\n{colors.white}{response['name']}{colors.reset}") + for attr in self.profile_attrs: + print(f'{colors.white}├─ {self.profile_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}') + + + # Fetching repository information + def repo_info(self): + username = input(f'{colors.white}@{colors.green}Owner-username{colors.white} >> {colors.reset}') + repo_name = input(f'{colors.white}%{colors.green}reponame{colors.white} >> {colors.reset}') + api = f'https://api.github.com/repos/{username}/{repo_name}' + response = requests.get(api).json() + print(f"\n{colors.white}{response['full_name']}{colors.reset}") + for attr in self.repo_attrs: + print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}") + + + # Get path contents + def path_contents(self): + username = input(f'{colors.white}@{colors.green}Owner-username{colors.white} >> {colors.reset}') + repo_name = input(f'{colors.white}%{colors.green}reponame{colors.white} >> {colors.reset}') + path_name = input(f'{colors.white}/path/name >>{colors.reset} ') + api = f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}' + response = requests.get(api) + if response.status_code != 200: + print(f'{colors.white}[{colors.red}-{colors.white}] [Repo/User] not found.{colors.reset}') + + response = response.json() + for item in response: + print(f"\n{colors.white}{item['name']}{colors.reset}") + for attr in self.path_attrs: + print(f'{colors.white}├─ {self.path_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}') + + + # Fetching organozation repositories + def org_repos(self): + organization = input(f'{colors.white}@{colors.green}Organization{colors.white} >> {colors.reset}') + api = f'https://api.github.com/orgs/{organization}/repos?per_page=100' + response = requests.get(api).json() + for repo in response: + print(f"\n{colors.white}{repo['full_name']}{colors.reset}") + for attr in self.repo_attrs: + print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{repo[attr]}{colors.reset}") + print('\n') + + + # Fetching user repositories + def user_repos(self): + username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') + api = f'https://api.github.com/users/{username}/repos?per_page=100' + response = requests.get(api).json() + for repo in response: + print(f"\n{colors.white}{repo['full_name']}{colors.reset}") + for attr in self.repo_attrs: + print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{repo[attr]}{colors.reset}") + print('\n') + + + # Fetching user's gists + def user_gists(self): + username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') + api = f'https://api.github.com/users/{username}/gists' + response = requests.get(api).json() + if response == []: + print(f'{colors.white}[{colors.red}-{colors.white}] @{username} does not have any active gists.{colors.reset}') + for item in response: + print(f"\n{colors.white}{item['id']}{colors.reset}") + for attr in self.gists_attrs: + print(f"{colors.white}├─ {self.gists_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Fetching user's followera' + def followers(self): + username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') + api = f'https://api.github.com/users/{username}/followers?per_page=100' + response = requests.get(api).json() + for item in response: + print(f"\n{colors.white}@{item['login']}{colors.reset}") + for attr in self.user_attrs: + print(f"{colors.white}├─ {self.user_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Checking whether or not user[A] follows user[B] + def following(self): + user_a = input(f'{colors.white}@{colors.green}User[A]{colors.white} >> {colors.reset}') + user_b = input(f'{colors.white}@{colors.green}User[B]{colors.white} >> {colors.reset}') + api = f'https://api.github.com/users/{user_a}/following/{user_b}' + response = requests.get(api) + if response.status_code == 204: + print(f'{colors.white}[{colors.green}+{colors.white}] @{user_a} follows @{user_b}.{colors.reset}') + else: + print(f'{colors.white}[{colors.red}-{colors.white}] @{user_a} does not follow @{user_b}.{colors.reset}') + + + # User search + def user_search(self): + query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') + api = f'https://api.github.com/search/users?q={query}&per_page=100' + response = requests.get(api).json() + for item in response['items']: + print(f"\n{colors.white}@{item['login']}{colors.reset}") + for attr in self.user_attrs: + print(f"{colors.white}├─ {self.user_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Repository search + def repo_search(self): + query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') + api = f'https://api.github.com/search/repositories?q={query}&per_page=100' + response = requests.get(api).json() + for item in response['items']: + print(f"\n{colors.white}{item['full_name']}{colors.reset}") + for attr in self.repo_attrs: + print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Topics search + def topic_search(self): + query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') + api = f'https://api.github.com/search/topics?q={query}&per_page=100' + response = requests.get(api).json() + for item in response['items']: + print(f"\n{colors.white}{item['name']}{colors.reset}") + for attr in self.topic_attrs: + print(f"{colors.white}├─ {self.topic_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Issue search + def issue_search(self): + query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') + api = f'https://api.github.com/search/issues?q={query}&per_page=100' + response = requests.get(api).json() + for item in response['items']: + print(f"\n{colors.white}{item['title']}{colors.reset}") + for attr in self.issue_attrs: + print(f"{colors.white}├─ {self.issue_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") + print('\n') + + + # Commits search + def commits_search(self): + query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') + api = f'https://api.github.com/search/commits?q={query}&per_page=100' + response = requests.get(api).json() + n=0 + for item in response['items']: + n+=1 + print(f'{colors.white}{n}.{colors.reset}') + pprint(item['commit']) + print('\n') + + + # Update program + def update(self): + logging.info('Checking for update(s)...') + files_to_update = ['src/main.py','lib/banner.py','lib/colors.py','octosuite'] + print(f'\n{colors.white}[{colors.green}*{colors.white}] Fetching update(s). Please wait...{colors.reset}') + for file in files_to_update: + data = urllib.request.urlopen(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}').read() + with open(file, 'wb') as code: + code.write(data) + code.close() + + logging.info('Update complete.') + exit(f'{color.white}[{colors.green}+{colors.white}] Update complete. Re-run octosuite.{colors.reset}') + + + # Author info + def author(self): + print(f'\n{colors.white}Richard Mwewa (Ritchie){colors.reset}') + for key,value in self.author_dict.items(): + print(f'{colors.white}├─ {key}: {colors.green}{value}{colors.reset}') + + + def help(self): + help = ''' + +usage: + orginfo --> Get organization info + userinfo --> Get user profile info + repoinfo --> Get user repository info + pathcontents --> Get contents of a specified path from a repository + orgrepos --> Get organization repositories + userrepos --> Get user repositories + usergists --> Get user gists + userfollowers --> Get user followers + userfollowing --> Check whether or not User[A] follows User[B] + usersearch --> Search user(s) + reposearch --> Search repositor[y][ies] + topicsearch --> Search topic(s) + issuesearch --> Search issue(s) + commitsearch --> Search commit(s) + update --> Check for/download update(s) + author --> Show author information + help --> Show usage/help + exit --> Exit session + ''' + return help + + +file_exists = os.path.exists('.logs') +if file_exists: + pass +else: + os.mkdir('.logs') + +# Set to automatically monitor and log network and user activity to .log folder +logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='[%(asctime)s] %(message)s',level=logging.DEBUG)