diff --git a/src/main.py b/src/main.py index 2cd4abb..e19576a 100644 --- a/src/main.py +++ b/src/main.py @@ -22,10 +22,32 @@ from tqdm import tqdm from pprint import pprint from lib.banner import banner from datetime import datetime -from lib.colors import red, white, green, reset +from lib.colors import red, white, green, green_bg, white_bg, red_bg, reset class octosuite: def __init__(self): + # A list of tuples, mapping commands to their respective functionalities + self.commands_base = [('info:org', self.org_info), + ('info:user', self.user_profile), + ('info:repo', self.repo_info), + ('path:contents', self.path_contents), + ('repos:org', self.org_repos), + ('repos:user', self.user_repos), + ('user:gists', self.user_gists), + ('user:followers', self.followers), + ('user:following', self.following), + ('search:users', self.user_search), + ('search:repos', self.repo_search), + ('search:topics', self.topic_search), + ('search:issues', self.issue_search), + ('search:commits', self.commits_search), + ('update', self.update), + ('changelog', self.changelog), + ('info:dev', self.author), + ('help', self.help), + ('exit', self.exit_session)] + + # Path attribute self.path_attrs =['size','type','path','sha','html_url'] # Path attribute dictionary @@ -35,6 +57,7 @@ class octosuite: '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 @@ -57,6 +80,8 @@ class octosuite: '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 @@ -88,6 +113,7 @@ class octosuite: '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 @@ -109,6 +135,7 @@ class octosuite: '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 @@ -119,6 +146,7 @@ class octosuite: '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'] @@ -130,6 +158,7 @@ class octosuite: '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'] @@ -143,6 +172,7 @@ class octosuite: '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 @@ -162,75 +192,44 @@ class octosuite: 'created_at': 'Created at', 'body': 'Body'} + # Author dictionary self.author_dict = {'Alias': 'rly0nheart', 'Country': 'Zambia, Africa', 'About.me': 'https://about.me/rly0nheart'} - + + def on_start(self): + # Start new session logging.info(f'Started new session on {platform.node()}:{os.getlogin()}') + + # Use 'cls' to clear screen on Windows based machines + # Otherwise, use 'clear' while True: - if platform.system() == 'Windows': - subprocess.run(['cls']) + if platform.system().lower().startswith(('win','darwin')): + subprocess.run(['cls']) else: subprocess.run(['clear'],shell=False) print(banner) - command = input(f'''{white}┌─({red}{os.getlogin()}{white}@{red}octosuite{white})-[{green}{os.getcwd()}{white}]\n└─╼[{green}:~{white}]{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 == 'changelog': - print(self.changelog()) - elif command == 'author': - self.author() - elif command == 'ilinso': - self.easter_egg() - elif command == 'help': - print(self.help()) - elif command == 'exit': - logging.info('Session terminated with \'exit\' command') - exit(f'\n{white}[{red}-{white}] Session terminated with \'exit\' command{reset}') - else: - print(f'\n{white}[{red}!{white}] Command not found: ‘{command}’{reset}') - logging.warning(f'command not found: ‘{command}’') - - input(f'\n{white}[{green}?{white}] Press any key to continue{reset} ') + command_input = input(f'''{white}┌───({red}{os.getlogin()}{white}@{red}octosuite{white})-[{green}{os.getcwd()}{white}]\n└─╼[{green}:~{white}]{reset} ''') + # Looping through the commands base to check if the user input command matches any command in the commands base, and return its functionality + # If no match is found, we ignore it + for command, functionality in self.commands_base: + if command == command_input: + functionality() + else: + pass + + input(f'\n{white}[{green} ? {white}] Press {white_bg}any key{reset}{white} to continue{reset} ') def org_info(self): - organization = input(f'{white}@{green}Organization {white}>>{reset} ') + organization = input(f'\n{white}[{white_bg}@Organization{reset}{white}] (username){reset} ') api = f'https://api.github.com/orgs/{organization}' response = requests.get(api) if response.status_code != 200: - print(f'\n{white}[{red}-{white}] Organization @{organization} {red}Not Found{reset}') + print(f'\n{white}[{red}-{white}] Organization @{organization} {red_bg}Not Found{reset}') else: response = response.json() print(f"\n{white}{response['name']}{reset}") @@ -240,11 +239,11 @@ class octosuite: # Fetching user information def user_profile(self): - username = input(f'{white}@{green}Username{white} >> {reset}') + username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ') api = f'https://api.github.com/users/{username}' response = requests.get(api) if response.status_code != 200: - print(f'\n{white}[{red}-{white}] User @{username} {red}Not Found{reset}') + print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}') else: response = response.json() print(f"\n{white}{response['name']}{reset}") @@ -254,12 +253,12 @@ class octosuite: # Fetching repository information def repo_info(self): - username = input(f'{white}@{green}Owner-username{white} >> {reset}') - repo_name = input(f'{white}%{green}reponame{white} >> {reset}') + repo_name = input(f'\n{white}[{white_bg}%reponame{reset}{white}]{reset} ') + username = input(f'{white}[{white_bg}@Owner{reset}{white}] (username){reset} ') api = f'https://api.github.com/repos/{username}/{repo_name}' response = requests.get(api) if response.status_code != 200: - print(f'\n{white}[{red}-{white}] Repository %{repo_name} {red}Not Found{reset}') + print(f'\n{white}[{red} - {white}] Repository %{repo_name} or user @{username} {red_bg}Not Found{reset}') else: response = response.json() print(f"\n{white}{response['full_name']}{reset}") @@ -269,13 +268,13 @@ class octosuite: # Get path contents def path_contents(self): - username = input(f'{white}@{green}Owner-username{white} >> {reset}') - repo_name = input(f'{white}%{green}reponame{white} >> {reset}') - path_name = input(f'{white}/path/name >>{reset} ') + username = input(f'\n{white}[{white_bg}@Owner{reset}{white}] (username){reset} ') + repo_name = input(f'{white}[{white_bg}%reponame{reset}{white}]{reset} ') + path_name = input(f'{white}[{white_bg}/path/name{reset}{white}]{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'\n{white}[{red}-{white}] Information {red}Not Found{reset}') + print(f'\n{white}[{red} - {white}] Information {red_bg}Not Found{reset}') else: response = response.json() for item in response: @@ -286,11 +285,11 @@ class octosuite: # Fetching organozation repositories def org_repos(self): - organization = input(f'{white}@{green}Organization{white} >> {reset}') + organization = input(f'\n{white}[{white_bg}@Organization{reset}{white}] (username){reset} ') api = f'https://api.github.com/orgs/{organization}/repos?per_page=100' response = requests.get(api) if response.status_code != 200: - print(f'\n{white}[{red}-{white}] Organization @{organization} {red}Not Found{reset}') + print(f'\n{white}[{red} - {white}] Organization @{organization} {red_bg}Not Found{reset}') else: response = response.json() for repo in response: @@ -302,11 +301,11 @@ class octosuite: # Fetching user repositories def user_repos(self): - username = input(f'{white}@{green}Username{white} >> {reset}') + username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ') api = f'https://api.github.com/users/{username}/repos?per_page=100' response = requests.get(api) if response.status_code != 200: - print(f'\n{white}[{red}-{white}] User @{username} {red}Not Found{reset}') + print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}') else: response = response.json() for repo in response: @@ -318,11 +317,13 @@ class octosuite: # Fetching user's gists def user_gists(self): - username = input(f'{white}@{green}Username{white} >> {reset}') + username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ') api = f'https://api.github.com/users/{username}/gists' response = requests.get(api).json() if response == []: - print(f'{white}[{red}-{white}]User @{username} does not have any active gists.{reset}') + print(f'\n{white}[{red} - {white}] User @{username} {red_bg}does not{reset}{white} have any active gists.{reset}') + elif "Not Found" in response['message']: + print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}') else: for item in response: print(f"\n{white}{item['id']}{reset}") @@ -333,11 +334,13 @@ class octosuite: # Fetching user's followera' def followers(self): - username = input(f'{white}@{green}Username{white} >> {reset}') + username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ') api = f'https://api.github.com/users/{username}/followers?per_page=100' response = requests.get(api).json() if response == []: - print(f'\n{white}[{red}-{white}]User @{username} does not have followers.{reset}') + print(f'\n{white}[{red} - {white}]User @{username} {red_bg}does not{reset}{white} have followers.{reset}') + elif "Not Found" in response['message']: + print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}') else: for item in response: print(f"\n{white}@{item['login']}{reset}") @@ -348,19 +351,19 @@ class octosuite: # Checking whether or not user[A] follows user[B] def following(self): - user_a = input(f'{white}@{green}User[A]{white} >> {reset}') - user_b = input(f'{white}@{green}User[B]{white} >> {reset}') + user_a = input(f'\n{white}[{white_bg}@User A{reset}{white}] (username){reset} ') + user_b = input(f'{white}[{white_bg}@User B{reset}{white}] (username){reset} ') api = f'https://api.github.com/users/{user_a}/following/{user_b}' response = requests.get(api) if response.status_code == 204: - print(f'{white}[{green}+{white}] @{user_a} follows @{user_b}.{reset}') + print(f'\n{white}[{green} + {white}] @{user_a} {green_bg}follows{reset}{white} @{user_b}.{reset}') else: - print(f'{white}[{red}-{white}] @{user_a} does not follow @{user_b}.{reset}') + print(f'\n{white}[{red} - {white}] @{user_a} {red_bg}does not{reset}{white} follow @{user_b}.{reset}') # User search def user_search(self): - query = input(f'{white}#{green}Query{white} >> {reset}') + query = input(f'\n{white}[{white_bg}#@Query{reset}{white}]{reset} ') api = f'https://api.github.com/search/users?q={query}&per_page=100' response = requests.get(api).json() for item in response['items']: @@ -372,7 +375,7 @@ class octosuite: # Repository search def repo_search(self): - query = input(f'{white}#{green}Query{white} >> {reset}') + query = input(f'\n{white}[{white_bg}#%Query{reset}{white}]{reset} ') api = f'https://api.github.com/search/repositories?q={query}&per_page=100' response = requests.get(api).json() for item in response['items']: @@ -384,7 +387,7 @@ class octosuite: # Topics search def topic_search(self): - query = input(f'{white}#{green}Query{white} >> {reset}') + query = input(f'\n{white}[{white_bg}##Query{reset}{white}]{reset} ') api = f'https://api.github.com/search/topics?q={query}&per_page=100' response = requests.get(api).json() for item in response['items']: @@ -396,7 +399,7 @@ class octosuite: # Issue search def issue_search(self): - query = input(f'{white}#{green}Query{white} >> {reset}') + query = input(f'\n{white}[{white_bg}#!Query{reset}{white}]{reset} ') api = f'https://api.github.com/search/issues?q={query}&per_page=100' response = requests.get(api).json() for item in response['items']: @@ -408,53 +411,45 @@ class octosuite: # Commits search def commits_search(self): - query = input(f'{white}#{green}Query{white} >> {reset}') + query = input(f'\n{white}[{white_bg}#:Query{reset}{white}]{reset} ') api = f'https://api.github.com/search/commits?q={query}&per_page=100' response = requests.get(api).json() - n=0 + number=0 for item in response['items']: - n+=1 - print(f'{white}{n}.{reset}') + number+=1 + print(f'{white}{number}.{reset}') pprint(item['commit']) print('\n') # Update program def update(self): - logging.info('Fetching updates...') - files_to_update = ['src/main.py','lib/banner.py','lib/colors.py','octosuite','.github/dependabot.yml','LICENSE','README.md','requirements.txt'] - for file in tqdm(files_to_update,desc=f'{white}[{green}*{white}] Updating{reset}'): + logging.info('Updating...') + files_to_update = ['src/main.py','lib/banner.py','lib/colors.py','octosuite','.github/dependabot.yml','.github/ISSUE_TEMPLATE/bug_report.md','.github/ISSUE_TEMPLATE/feature_request.md','.github/ISSUE_TEMPLATE/config.yml','LICENSE','README.md','requirements.txt'] + for file in tqdm(files_to_update,desc=f'{white}[{green} * {white}] Updating{reset}'): data = requests.get(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}') with open(file, 'wb') as code: code.write(data.content) code.close() logging.info('Update complete.') - exit(f'{white}[{green}+{white}] Updated successfully. Re-run octosuite.{reset}') - - - def easter_egg(self): - print(f'\n{white}[{green}*{white}] Downloading. Please wait...{reset}') - file = requests.get('https://drive.google.com/uc?export=download&id=1IRu4kWSuNpYWH8hZkqQ8mLnv4sSDu-GN') - with open('EasterEgg.zip','wb') as f: - f.write(file.content) - - exit(f'{white}[{green}+{white}] Downloaded (EasterEgg.zip).\n{white}[{green}!{white}] The password is: {green}horus{white}\n[{green}!{white}] Happy hunting! :).{reset}') + exit(f'{white}[{green} + {white}] {green_bg}Updated{reset}{white} successfully. Re-run octosuite.{reset}') # Show changelog def changelog(self): # lol yes the changelog is hard coded - changelog_text = ''' - v1.5.1-beta CHANGELOG: - -• First pypi package release -• Termux users will now have to manually create the .logs folder -• Changed logs date/time format -• Removed 1 internal dependency -• There's an easter egg somewhere in here ;) (use the command 'ilinso') -''' - return changelog_text + changelog_text = f''' + + {red_bg}v1.5.2-alpha [CHANGELOG]{reset} + • Users will now get to choose whether to enable colors or not + • Cleaned code + • Improved perfomance + • Will be ignoring unknown commands instead of printing the error + • Major bug fixes + {red_bg} {reset} + ''' + print(changelog_text) # Author info @@ -464,46 +459,51 @@ class octosuite: print(f'{white}├─ {key}: {green}{value}{reset}') + def exit_session(self): + logging.info('Session closed with \'exit\' command.') + exit(f'\n{white}[{green} ! {white}] Session closed with {white_bg}exit{reset}{white} command.{reset}') + + def help(self): help = f''' - -help: - {white}Command Descritption - ------------ --------------------------------------------------------- - {green}orginfo{white} --> Get target organization info{reset} - {green}userinfo{white} --> Get target user profile info{reset} - {green}repoinfo{white} --> Get target repository info{reset} - {green}pathcontents{white} --> Get contents of a specified path from a target repository{reset} - {green}orgrepos{white} --> Get a list of repositories owned by a target organization{reset} - {green}userrepos{white} --> Get a list of repositories owned by a target user{reset} - {green}usergists{white} --> Get a list of gists owned by a target user{reset} - {green}userfollowers{white} --> Get a list of the target's followers{reset} - {green}userfollowing{white} --> Check whether or not User[A] follows User[B]{reset} - {green}usersearch{white} --> Search user(s){reset} - {green}reposearch{white} --> Search repositor[y][ies]{reset} - {green}topicsearch{white} --> Search topic(s){reset} - {green}issuesearch{white} --> Search issue(s){reset} - {green}commitsearch{white} --> Search commit(s){reset} - {green}update{white} --> Update octosuite{reset} - {green}changelog{white} --> Show changelog{reset} - {green}author{white} --> Show author info{reset} - {green}help{white} --> Show usage/help{reset} - {green}exit{white} --> Exit session{reset} - {white}------------ ---------------------------------------------------------{reset} + {red_bg}[COMMAND] [DESCRIPTION] {reset} + info:org Get target organization info + info:user Get target user profile info + info:repo Get target repository info + info:dev Show developer's info + path:contents Get contents of a specified path from a target repository + repos:org Get a list of repositories owned by a target organization + repos:user Get a list of repositories owned by a target user + user:gists Get a list of gists owned by a target user + user:followers Get a list of the target's followers + user:following Check whether or not User[A] follows User[B] + search:users Search user(s) + search:repos Search repositor[y][ies] + search:topics Search topic(s) + search:issues Search issue(s) + search:commits Search commit(s) + update Update octosuite + changelog Show changelog + help Show usage/help + exit Exit session + {red_bg} {reset} ''' - return help + print(help) +# If .logs folder exists, pass if os.path.exists('.logs'): pass else: # Creating the .logs directory - if platform.system() == "Windows": + # If the current system is Windows based, we run mkdir command without sudo + # Else we run the mkdir command with sudo + if platform.system().lower().startswith(('win','darwin')): subprocess.run(['mkdir','.logs']) else: subprocess.run(['sudo','mkdir','.logs'],shell=False) # Set to automatically monitor and log network and user activity into the .logs folder -logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='%(asctime)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S',level=logging.DEBUG) +logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='[%(asctime)s] [%(levelname)s] %(message)s',datefmt='%Y-%m-%d %H:%M:%S%p',level=logging.DEBUG)