diff --git a/README.md b/README.md index 69496d1..c00d494 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,8 @@ - [x] ...And more ## Note -> octosuite automatically logs network and minor user activity of each session. The logs are saved by date and time in the .logs folder ->> If you believe octosuite can be better, feel free to open a pull request with your improvementsβœŒπŸΎπŸ™‚ +> Octosuite automatically logs network and user activity of each session, the logs are saved by date and time in the .logs folder + ## License ![license](https://user-images.githubusercontent.com/74001397/137917929-2f2cdb0c-4d1d-4e4b-9f0d-e01589e027b5.png) diff --git a/octosuite/banner.py b/octosuite/banner.py index 151135b..13cef79 100644 --- a/octosuite/banner.py +++ b/octosuite/banner.py @@ -1,11 +1,11 @@ import getpass from octosuite.colors import red, white, green, reset -''' +""" banner.py This file holds the program's banner logo and version tag -''' -version_tag = "2.2.2" +""" +version_tag = "2.2.3" name_logo = f"""{white} _______ __ _______ __ __ | |.----.| |_.-----.| __|.--.--.|__| |_.-----. diff --git a/octosuite/colors.py b/octosuite/colors.py index 7c6f233..5a29de1 100644 --- a/octosuite/colors.py +++ b/octosuite/colors.py @@ -5,39 +5,39 @@ from datetime import datetime # This file gets called first at start up before any other file gets called # colors.py is the reason why users get to choose whether to enable/disable colors system_info = [("Processor",platform.processor), - ("Node", platform.node), - ("Release", platform.release), - ("Architecture", platform.architecture), - ("Version", platform.version)] + ("Node", platform.node), + ("Release", platform.release), + ("Architecture", platform.architecture), + ("Version", platform.version)] banner = f""" - OCTOSUITE Β© 2022 Richard Mwewa - {datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')} - - """ + OCTOSUITE Β© 2022 Richard Mwewa + {datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')} + + """ print(banner) print(f"\t{platform.system()}") for key, value in system_info: - print(f"\tβ”œβ”€ {key}: {value()}") + print(f"\tβ”œβ”€ {key}: {value()}") print("\n") while True: - try: - color_chooser = input(f"[ ? ] Welcome, would you like to enable colors for this session? (Y/n) ").lower() - if color_chooser == "y": - header_title = "bold white" - red = "[red]" - white = "[white]" - green = "[green]" - red_bold = "[white bold]" - white_bold = "[white bold]" - green_bold = "[green bold]" - reset = "[/]" - break - elif color_chooser == "n": - header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" - break - else: - print(f"\n[ ! ] Your response '{color_chooser}' is invalid (expected y or n)") - - except KeyboardInterrupt: - exit(f"[ ! ] Process interrupted with [Ctrl+C].") + try: + color_chooser = input(f"[ ? ] Welcome, would you like to enable colors for this session? (Y/n) ").lower() + if color_chooser == "y": + header_title = "bold white" + red = "[red]" + white = "[white]" + green = "[green]" + red_bold = "[white bold]" + white_bold = "[white bold]" + green_bold = "[green bold]" + reset = "[/]" + break + elif color_chooser == "n": + header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" + break + else: + print(f"\n[ ! ] Your response '{color_chooser}' is invalid (expected y or n)") + + except KeyboardInterrupt: + exit(f"[ ! ] Process interrupted with [Ctrl+C].") diff --git a/octosuite/csv_loggers.py b/octosuite/csv_loggers.py index d4e8785..2f8b0a1 100644 --- a/octosuite/csv_loggers.py +++ b/octosuite/csv_loggers.py @@ -5,10 +5,11 @@ from octosuite.sign_vars import SignVar from octosuite.log_roller import logRoller from octosuite.colors import red, white, green, reset -''' +""" csvLogger This class holds the methods for creating .csv files of each functionality in main -''' +""" + class csvLogger: # .csv for organization' profile def logOrgProfile(response): diff --git a/octosuite/helper.py b/octosuite/helper.py index 0fb0709..cfb68ae 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -1,93 +1,106 @@ from rich.table import Table from rich import print as xprint from octosuite.colors import white, green, white_bold, green_bold, header_title, reset + """ Help This class holds the help text for available commands. """ + + class Help: usageText = 'Use syntax {} to get started with %s{}%s.' % (green_bold, reset) usageText1 = '%sUse {} to view all available subcommands.%s' % (white, reset) usageText2 = "%sThe {} command works with subcommands. %s" % (white, reset) - + def Org(): - xprint(Help.usageText2.format(f"{green_bold}org{reset}") + Help.usageText1.format(f"{green_bold}help:org{reset}")) - - + xprint( + Help.usageText2.format(f"{green_bold}org{reset}") + Help.usageText1.format(f"{green_bold}help:org{reset}")) + + def Repo(): - xprint(Help.usageText2.format(f"{green_bold}repo{reset}") + Help.usageText1.format(f"{green_bold}help:repo{reset}")) - - + xprint(Help.usageText2.format(f"{green_bold}repo{reset}") + Help.usageText1.format( + f"{green_bold}help:repo{reset}")) + + def User(): - xprint(Help.usageText2.format(f"{green_bold}user{reset}") + Help.usageText1.format(f"{green_bold}help:user{reset}")) - + xprint(Help.usageText2.format(f"{green_bold}user{reset}") + Help.usageText1.format( + f"{green_bold}help:user{reset}")) + + def Search(): - xprint(Help.usageText2.format(f"{green_bold}search{reset}") + Help.usageText1.format(f"{green_bold}help:search{reset}")) - - + xprint(Help.usageText2.format(f"{green_bold}search{reset}") + Help.usageText1.format( + f"{green_bold}help:search{reset}")) + + def Source(): - xprint(Help.usageText2.format(f"{green_bold}source{reset}") + Help.usageText1.format(f"{green_bold}help:source{reset}")) - + xprint(Help.usageText2.format(f"{green_bold}source{reset}") + Help.usageText1.format( + f"{green_bold}help:source{reset}")) + + def Logs(): - xprint(Help.usageText2.format(f"{green_bold}logs{reset}") + Help.usageText1.format(f"{green_bold}help:logs{reset}")) - - + xprint(Help.usageText2.format(f"{green_bold}logs{reset}") + Help.usageText1.format( + f"{green_bold}help:logs{reset}")) + + def Version(): - xprint(Help.usageText2.format(f"{green_bold}version{reset}") + Help.usageText1.format(f"{green_bold}help:version{reset}")) - - + xprint(Help.usageText2.format(f"{green_bold}version{reset}") + Help.usageText1.format( + f"{green_bold}help:version{reset}")) + + def Csv(): - xprint(Help.usageText2.format(f"{green_bold}csv{reset}") + Help.usageText1.format(f"{green_bold}help:csv{reset}")) - - + xprint( + Help.usageText2.format(f"{green_bold}csv{reset}") + Help.usageText1.format(f"{green_bold}help:csv{reset}")) + + def versionCommand(): - version_cmd_table =Table(show_header=True, header_style=header_title) + version_cmd_table = Table(show_header=True, header_style=header_title) version_cmd_table.add_column("Command", style="dim", width=12) version_cmd_table.add_column("Description") - version_cmd_table.add_row("check", "Check for new release(s)") + version_cmd_table.add_row("check", "Check for new release(s)") version_cmd_table.add_row("info", "Version information") syntax = f"{green}version:{reset}" xprint(f"{Help.usageText.format(syntax, 'version management')}") xprint(version_cmd_table) - - + + def sourceCommand(): - source_cmd_table =Table(show_header=True, header_style=header_title) + source_cmd_table = Table(show_header=True, header_style=header_title) source_cmd_table.add_column("Command", style="dim", width=12) source_cmd_table.add_column("Description") - source_cmd_table.add_row("zipball", "Download source code Zipball") + source_cmd_table.add_row("zipball", "Download source code Zipball") source_cmd_table.add_row("tarball", "Download source code Tarball") syntax = f"{green}source:{reset}" xprint(f"{Help.usageText.format(syntax, 'source code downloads')}") xprint(source_cmd_table) - - + + def searchCommand(): - search_cmd_table =Table(show_header=True, header_style=header_title) + search_cmd_table = Table(show_header=True, header_style=header_title) search_cmd_table.add_column("Command", style="dim", width=12) search_cmd_table.add_column("Description") - search_cmd_table.add_row("users", "Search user(s)") + search_cmd_table.add_row("users", "Search user(s)") search_cmd_table.add_row("repos", "Search repositor[y][ies]") search_cmd_table.add_row("topics", "Search topic(s)") search_cmd_table.add_row("issues", "Search issue(s)") - search_cmd_table.add_row("commits", "Search commit(s)") + search_cmd_table.add_row("commits", "Search commit(s)") syntax = f"{green}search:{reset}" xprint(f"{Help.usageText.format(syntax, 'target discovery')}") xprint(search_cmd_table) - - + + def userCommand(): - user_cmd_table =Table(show_header=True, header_style=header_title) + user_cmd_table = Table(show_header=True, header_style=header_title) user_cmd_table.add_column("Command", style="dim", width=12) user_cmd_table.add_column("Description") - user_cmd_table.add_row("profile", "Get a target's profile info") + user_cmd_table.add_row("profile", "Get a target's profile info") user_cmd_table.add_row("gists", "Return a users's gists") user_cmd_table.add_row("org", "Return organizations that a target belongs to/owns") user_cmd_table.add_row("repos", "Return a target's repositories") - user_cmd_table.add_row("events", "Return a target's events") + user_cmd_table.add_row("events", "Return a target's events") user_cmd_table.add_row("follows", "Check if user(A) follows user(B)") user_cmd_table.add_row("followers", "Return a target's followers") user_cmd_table.add_row("following", "Return a list of users the target is following") @@ -96,13 +109,13 @@ class Help: syntax = f"{green}user:{reset}" xprint(f"{Help.usageText.format(syntax, 'user investigation(s)')}") xprint(user_cmd_table) - - + + def orgCommand(): - org_cmd_table =Table(show_header=True, header_style=header_title) + org_cmd_table = Table(show_header=True, header_style=header_title) org_cmd_table.add_column("Command", style="dim", width=12) org_cmd_table.add_column("Description") - org_cmd_table.add_row("profile", "Get a target organization' profile info") + org_cmd_table.add_row("profile", "Get a target organization' profile info") org_cmd_table.add_row("repos", "Return a target organization' repositories") org_cmd_table.add_row("events", "Return a target organization' events") org_cmd_table.add_row("member", "Check if a specified user is a public member of the target organization") @@ -110,68 +123,68 @@ class Help: syntax = f"{green}org:{reset}" xprint(f"{Help.usageText.format(syntax, 'organization investigation(s)')}") xprint(org_cmd_table) - - + + def repoCommand(): - repo_cmd_table =Table(show_header=True, header_style=header_title) + repo_cmd_table = Table(show_header=True, header_style=header_title) repo_cmd_table.add_column("Command", style="dim", width=12) repo_cmd_table.add_column("Description") - repo_cmd_table.add_row("profile", "Get a repository's info") + repo_cmd_table.add_row("profile", "Get a repository's info") repo_cmd_table.add_row("issues", "Return a repository's issues") repo_cmd_table.add_row("forks", "Return a repository's forks") repo_cmd_table.add_row("releases", "Return a repository's releases") - repo_cmd_table.add_row("stargazers", "Return a repository's stargazers") + repo_cmd_table.add_row("stargazers", "Return a repository's stargazers") repo_cmd_table.add_row("path_contents", "List contents in a path of a repository") syntax = f"{green}repo:{reset}" xprint(f"{Help.usageText.format(syntax, 'repository investigation(s)')}") xprint(repo_cmd_table) - - + + def logsCommand(): - logs_cmd_table =Table(show_header=True, header_style=header_title) + logs_cmd_table = Table(show_header=True, header_style=header_title) logs_cmd_table.add_column("Command", style="dim", width=12) logs_cmd_table.add_column("Description") - logs_cmd_table.add_row("view", "View logs") + logs_cmd_table.add_row("view", "View logs") logs_cmd_table.add_row("read", "Read log") logs_cmd_table.add_row("delete", "Delete log") syntax = f"{green}logs:{reset}" xprint(f"{Help.usageText.format(syntax, 'log(s) management')}") xprint(logs_cmd_table) - + def csvCommand(): - csv_cmd_table =Table(show_header=True, header_style=header_title) + csv_cmd_table = Table(show_header=True, header_style=header_title) csv_cmd_table.add_column("Command", style="dim", width=12) csv_cmd_table.add_column("Description") - csv_cmd_table.add_row("view", "View csv files") + csv_cmd_table.add_row("view", "View csv files") csv_cmd_table.add_row("read", "Read csv") csv_cmd_table.add_row("delete", "Delete csv") syntax = f"{green}csv:{reset}" xprint(f"{Help.usageText.format(syntax, 'csv management')}") xprint(csv_cmd_table) - + def helpCommand(): - core_cmd_table =Table(show_header=True, header_style=header_title) + core_cmd_table = Table(show_header=True, header_style=header_title) core_cmd_table.add_column("Command", style="dim", width=12) core_cmd_table.add_column("Description") - core_cmd_table.add_row("help", "Help menu") - core_cmd_table.add_row("exit", "Close session") + core_cmd_table.add_row("help", "Help menu") + core_cmd_table.add_row("exit", "Close session") core_cmd_table.add_row("clear", "Clear screen") - core_cmd_table.add_row("about", "Program's info") + core_cmd_table.add_row("about", "Program's info") core_cmd_table.add_row("author", "Developer's info") - - help_sub_cmd_table =Table(show_header=True, header_style=header_title) + + help_sub_cmd_table = Table(show_header=True, header_style=header_title) help_sub_cmd_table.add_column("Command", style="dim", width=12) help_sub_cmd_table.add_column("Description") help_sub_cmd_table.add_row("csv", "List all csv management commands") help_sub_cmd_table.add_row("logs", "List all logs management commands") help_sub_cmd_table.add_row("org", "List all organization investigation commands") help_sub_cmd_table.add_row("user", "List all users investigation commands") - help_sub_cmd_table.add_row("repo", "List all repository investigation commands") + help_sub_cmd_table.add_row("repo", "List all repository investigation commands") help_sub_cmd_table.add_row("search", "List all target discovery commands") help_sub_cmd_table.add_row("source", "List all source code download commands (for developers)") help_sub_cmd_table.add_row("version", "List all version management commands") diff --git a/octosuite/log_roller.py b/octosuite/log_roller.py index b60a73e..3ee5289 100644 --- a/octosuite/log_roller.py +++ b/octosuite/log_roller.py @@ -1,8 +1,9 @@ """ -logRoller -This class is where the main notification strings/messages are held, -and are being used in two different cases (they're beig used by logging to be written to log files, and being printed out to the screen). +logRoller This class is where the main notification strings/messages are held, and are being used in two different +cases (they're being used by logging to be written to log files, and being printed out to the screen). """ + + class logRoller: Ctrl = "Session terminated with {}." Error = "An error occurred: {}" diff --git a/octosuite/main.py b/octosuite/main.py index 37fd3c6..157b7cd 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -1,9 +1,7 @@ #!usr/bin/python -import os -import csv +import os import sys -import json import logging import getpass import requests @@ -17,23 +15,24 @@ from octosuite.sign_vars import SignVar from octosuite.log_roller import logRoller from octosuite.csv_loggers import csvLogger from octosuite.banner import name_logo, version_tag -from octosuite.colors import red, white, green, red_bold, white_bold, green_bold, header_title, reset - +from octosuite.colors import red, white, green, white_bold, green_bold, header_title, reset # API endpoint endpoint = 'https://api.github.com' # Path attribute -path_attrs =['size','type','path','sha','html_url'] +path_attrs = ['size', 'type', 'path', 'sha', 'html_url'] # Path attribute dictionary path_attr_dict = {'size': 'Size (bytes)', 'type': 'Type', 'path': 'Path', 'sha': 'SHA', 'html_url': 'URL'} - - + + # Organization attributes -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'] +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 org_attr_dict = {'avatar_url': 'Profile Photo', 'login': 'Username', @@ -54,10 +53,13 @@ org_attr_dict = {'avatar_url': 'Profile Photo', 'has_repository_projects': 'Has repository projects?', 'created_at': 'Created at', 'updated_at': 'Updated at'} - - + + # Repository attributes -repo_attrs = ['id','description','forks','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','fork','allow_forking','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at'] +repo_attrs = ['id', 'description', 'forks', 'stargazers_count', 'watchers', 'license', 'default_branch', 'visibility', + 'language', 'open_issues', 'topics', 'homepage', 'clone_url', 'ssh_url', 'fork', 'allow_forking', + 'private', 'archived', 'has_downloads', 'has_issues', 'has_pages', 'has_projects', 'has_wiki', + 'pushed_at', 'created_at', 'updated_at'] # Repository attribute dictionary repo_attr_dict = {'id': 'ID', 'description': 'About', @@ -86,10 +88,11 @@ repo_attr_dict = {'id': 'ID', 'pushed_at': 'Pushed at', 'created_at': 'Created at', 'updated_at': 'Updated at'} - - + + # Repo releases attributes -repo_releases_attrs = ['id', 'node_id','tag_name','target_commitish','assets','draft','prerelease','created_at','published_at'] +repo_releases_attrs = ['id', 'node_id', 'tag_name', 'target_commitish', 'assets', 'draft', 'prerelease', 'created_at', + 'published_at'] # Repo releases attribute dictionary repo_releases_attr_dict = {'id': 'ID', 'node_id': 'Node ID', @@ -100,10 +103,12 @@ repo_releases_attr_dict = {'id': 'ID', 'prerelease': 'Is prerelease?', 'created_at': 'Created at', 'published_at': 'Published at'} - - + + # Profile attributes -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_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 profile_attr_dict = {'avatar_url': 'Profile Photo', 'login': 'Username', @@ -122,10 +127,10 @@ profile_attr_dict = {'avatar_url': 'Profile Photo', 'site_admin': 'Is site admin?', 'created_at': 'Joined at', 'updated_at': 'Updated at'} - - + + # User attributes -user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url'] +user_attrs = ['avatar_url', 'id', 'node_id', 'gravatar_id', 'site_admin', 'type', 'html_url'] # User attribute dictionary user_attr_dict = {'avatar_url': 'Profile Photo', 'id': 'ID', @@ -134,10 +139,10 @@ user_attr_dict = {'avatar_url': 'Profile Photo', 'site_admin': 'Is site admin?', 'type': 'Account type', 'html_url': 'URL'} - - + + # Topic atrributes -topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at'] +topic_attrs = ['score', 'curated', 'featured', 'display_name', 'created_by', 'created_at', 'updated_at'] # Topic attribute dictionary topic_attr_dict = {'score': 'Score', 'curated': 'Curated', @@ -146,10 +151,10 @@ topic_attr_dict = {'score': 'Score', 'created_by': 'Created by', 'created_at': 'Created at', 'updated_at': 'Updated at'} - - + + # Gists attributes -gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at'] +gists_attrs = ['node_id', 'description', 'comments', 'files', 'git_push_url', 'public', 'truncated', 'updated_at'] # Gists attribute dictionary gists_attr_dict = {'node_id': 'Node ID', 'description': 'About', @@ -159,10 +164,11 @@ gists_attr_dict = {'node_id': 'Node ID', 'public': 'Is public?', 'truncated': 'Is truncated?', 'updated_at': 'Updated at'} - - + + # Issue attributes -issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at'] +issue_attrs = ['id', 'node_id', 'score', 'state', 'number', 'comments', 'milestone', 'assignee', 'assignees', 'labels', + 'locked', 'draft', 'closed_at'] # Issue attribute dict issue_attr_dict = {'id': 'ID', 'node_id': 'Node ID', @@ -178,10 +184,12 @@ issue_attr_dict = {'id': 'ID', 'draft': 'Is draft?', 'locked': 'Is locked?', 'created_at': 'Created at'} - - + + # Repo issues attributes -repo_issues_attrs = ['id','node_id','state', 'reactions','number','comments','milestone','assignee','active_lock_reason', 'author_association','assignees','labels','locked','closed_at','created_at','updated_at'] +repo_issues_attrs = ['id', 'node_id', 'state', 'reactions', 'number', 'comments', 'milestone', 'assignee', + 'active_lock_reason', 'author_association', 'assignees', 'labels', 'locked', 'closed_at', + 'created_at', 'updated_at'] # Issue attribute dict repo_issues_attr_dict = {'id': 'ID', 'node_id': 'Node ID', @@ -199,23 +207,23 @@ repo_issues_attr_dict = {'id': 'ID', 'closed_at': 'Closed at', 'created_at': 'Created at', 'updated_at': 'Updated at'} - - + + # User organizations attributes -user_orgs_attrs = ['avatar_url','id','node_id','url','description'] +user_orgs_attrs = ['avatar_url', 'id', 'node_id', 'url', 'description'] user_orgs_attr_dict = {'avatar_url': 'Profile Photo', 'id': 'ID', 'node_id': 'Node ID', 'url': 'URL', 'description': 'About'} - - + + # Author dictionary author_dict = {'Alias': 'rly0nheart', 'Country': ':zambia: Zambia, Africa', 'About.me': 'https://about.me/rly0nheart', 'Buy Me A Coffee': 'https://buymeacoffee.com/189381184'} - + ''' pathFinder() @@ -223,45 +231,52 @@ This function is responsible for creating/checking the availability of the (.lo enabling logging to automatically log network/user activity to a file, and logging the start of a session. ''' + + def pathFinder(): - ''' + + """ Here we create/check 3 directories (.logs, output, downloads) on startup - If they exists, we ignore, otherwise, we create them - ''' + If they exist, we ignore, otherwise, we create them + """ + directory_list = ['.logs', 'output', 'downloads'] for directory in directory_list: os.makedirs(directory, exist_ok=True) - + ''' Configure logging to log activities to a file, which will be named by the date and time a session was opened. ''' now = datetime.now() - now_formatted = now.strftime("%Y-%m-%d %H:%M:%S%p") - logging.basicConfig(filename=f".logs/{now_formatted}.log", format="[%(asctime)s] [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S%p", level=logging.DEBUG) + now_formatted = now.strftime("%Y-%m-%d %H-%M-%S%p") + logging.basicConfig(filename=f".logs/{now_formatted}.log", format="[%(asctime)s] [%(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S%p", level=logging.DEBUG) # Log the start of a session logging.info(logRoller.sessionOpened.format(platform.node(), getpass.getuser())) - + ''' onStart() This is the main function, responsible for mapping commands, calling other functions, and catching exceptions ''' + + def onStart(): pathFinder() # A list of tuples mapping commands to their functions command_map = [("exit", exitSession), - ("clear",clearScreen), + ("clear", clearScreen), ("about", about), ("author", author), ("help", Help.helpCommand), ("help:version", Help.versionCommand), ("help:source", Help.sourceCommand), ("help:search", Help.searchCommand), - ("help:user", Help.userCommand), - ("help:repo", Help.repoCommand), - ("help:logs", Help.logsCommand), + ("help:user", Help.userCommand), + ("help:repo", Help.repoCommand), + ("help:logs", Help.logsCommand), ("help:csv", Help.csvCommand), - ("help:org", Help.orgCommand), + ("help:org", Help.orgCommand), ("version", Help.Version), ("version:info", versionInfo), ("version:check", versionCheck), @@ -298,15 +313,15 @@ def onStart(): ("search:issues", issueSearch), ("search:commits", commitsSearch), ("logs", Help.Logs), - ("logs:view",viewLogs), - ("logs:read",readLog), - ("logs:delete",deleteLog), + ("logs:view", viewLogs), + ("logs:read", readLog), + ("logs:delete", deleteLog), ("csv", Help.Csv), ("csv:view", viewCsv), ("csv:read", readCsv), ("csv:delete", deleteCsv)] - - + + xprint(name_logo) ''' Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) @@ -314,7 +329,10 @@ def onStart(): ''' while True: try: - xprint(f"{white}β”Œβ”€β”€({red}{getpass.getuser()}{white}@{red}octosuite{white})\nβ”œβ”€β”€[~{green}{os.getcwd()}{white}]\nβ””β•Ό {reset}", end="");command_input = input().lower() + xprint( + f"{white}β”Œβ”€β”€({red}{getpass.getuser()}{white}@{red}octosuite{white})\nβ”œβ”€β”€[~{green}{os.getcwd()}{white}]\nβ””β•Ό {reset}", + end="") + command_input = input().lower() print("\n") ''' Iterating over the command_map and check if the user input matches any command in it [command_map], @@ -326,37 +344,40 @@ def onStart(): print("\n") else: pass - + # This catches the KeyboardInterrupt exception (Ctrl+C) except KeyboardInterrupt: logging.warning(logRoller.Ctrl.format("Ctrl+C")) xprint(f"{SignVar.warning} {logRoller.Ctrl.format('Ctrl+C')}") break - + # This initially catches all exceptions (except the KeyboardInterrupt) except Exception as e: logging.error(logRoller.Error.format(e)) - xprint(f"{SignVar.error} {logRoller.Error.format(e)}") - - -# Fetching organization info + xprint(f"{SignVar.error} {logRoller.Error.format(e)}") + + # Fetching organization info + + def orgProfile(): - xprint(f"{white}>> @{green}Organization {white}(username){reset} ", end="");organization = input() + xprint(f"{white}>> @{green}Organization {white}(username){reset} ", end="") + organization = input() response = requests.get(f"{endpoint}/orgs/{organization}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") elif response.status_code == 200: xprint(f"\n{white}{response.json()['name']}{reset}") for attr in org_attrs: - xprint(f"{white}β”œβ”€ {org_attr_dict[attr]}:{reset} {response.json()[attr]}") + xprint(f"{white}β”œβ”€ {org_attr_dict[attr]}:{reset} {response.json()[attr]}") csvLogger.logOrgProfile(response) else: xprint(response.json()) - - + + # Fetching user information def userProfile(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() response = requests.get(f"{endpoint}/users/{username}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") @@ -367,12 +388,14 @@ def userProfile(): csvLogger.logUserProfile(response) else: xprint(response.json()) - - + + # Fetching repository information def repoProfile(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username) ", end="") + username = input() response = requests.get(f"{endpoint}/repos/{username}/{repo_name}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") @@ -383,13 +406,16 @@ def repoProfile(): csvLogger.logRepoProfile(response) else: xprint(response.json()) - - + + # Get path contents def pathContents(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() - xprint(f"{white}>> ~{green}/path/name{reset} ", end="");path_name = input() + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username) ", end="") + username = input() + xprint(f"{white}>> ~{green}/path/name{reset} ", end="") + path_name = input() response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contents/{path_name}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.infoNotFound.format(repo_name, username, path_name)}") @@ -399,18 +425,21 @@ def pathContents(): content_count += 1 xprint(f"\n{white}{content['name']}{reset}") for attr in path_attrs: - xprint(f"{white}β”œβ”€ {path_attr_dict[attr]}:{reset} {content[attr]}") + xprint(f"{white}β”œβ”€ {path_attr_dict[attr]}:{reset} {content[attr]}") csvLogger.logRepoPathContents(content, repo_name) xprint(SignVar.info, f"Found {content_count} file(s) in {repo_name}/{path_name}.") else: xprint(response.json()) - - + + # repo contributors def repoContributors(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("contributors"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username) ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("contributors"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") @@ -422,13 +451,16 @@ def repoContributors(): csvLogger.logRepoContributors(contributor, repo_name) else: xprint(response.json()) - - + + # repo stargazers def repoStargazers(): - xprint(f"{white}>> %{green}Repository{reset} ");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repository stargazers"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repository stargazers"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") @@ -437,18 +469,21 @@ def repoStargazers(): elif response.status_code == 200: for stargazer in response.json(): xprint(f"\n{white}{stargazer['login']}{reset}") - for attr in user_attrs: + for attr in user_attrs: xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {stargazer[attr]}") csvLogger.logRepoStargazers(stargazer, repo_name) else: xprint(response.json()) - - + + # repo forks def repoForks(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repository forks"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repository forks"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") @@ -459,7 +494,7 @@ def repoForks(): for fork in response.json(): count += 1 xprint(f"\n{white}{fork['full_name']}{reset}") - for attr in repo_attrs: + for attr in repo_attrs: xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {fork[attr]}") csvLogger.logRepoForks(fork, count) else: @@ -468,276 +503,313 @@ def repoForks(): # Repo issues def repoIssues(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repository issues"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repository issues"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") elif response.json() == []: xprint(f"{SignVar.negative} Repository does not have open issues -> ({repo_name})") elif response.status_code == 200: - for issue in response.json(): - xprint(f"\n{white}{issue['title']}{reset}") - for attr in repo_issues_attrs: - xprint(f"{white}β”œβ”€ {repo_issues_attr_dict[attr]}:{reset} {issue[attr]}") - xprint(issue['body']) - csvLogger.logRepoIssues(issue, repo_name) + for issue in response.json(): + xprint(f"\n{white}{issue['title']}{reset}") + for attr in repo_issues_attrs: + xprint(f"{white}β”œβ”€ {repo_issues_attr_dict[attr]}:{reset} {issue[attr]}") + xprint(issue['body']) + csvLogger.logRepoIssues(issue, repo_name) else: xprint(response.json()) # Repo releases def repoReleases(): - xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repository releases"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Repository{reset} ", end="") + repo_name = input() + xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repository releases"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}") if response.status_code == 404: - xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") + xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") elif response.json() == []: xprint(f"{SignVar.negative} Repository does not have releases -> ({repo_name})") elif response.status_code == 200: for release in response.json(): - xprint(f"\n{white}{release['name']}{reset}") - for attr in repo_releases_attrs: - xprint(f"{white}β”œβ”€ {repo_releases_attr_dict[attr]}:{reset} {release[attr]}") - xprint(release['body']) - csvLogger.logRepoReleases(release, repo_name) + xprint(f"\n{white}{release['name']}{reset}") + for attr in repo_releases_attrs: + xprint(f"{white}β”œβ”€ {repo_releases_attr_dict[attr]}:{reset} {release[attr]}") + xprint(release['body']) + csvLogger.logRepoReleases(release, repo_name) else: xprint(response.json()) - - + + # Fetching organization repositories def orgRepos(): - xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() - xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="") + organization = input() + xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/orgs/{organization}/repos?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") elif response.status_code == 200: for repository in response.json(): - xprint(f"\n{white}{repository['full_name']}{reset}") - for attr in repo_attrs: - xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") - csvLogger.logOrgRepos(repository, organization) + xprint(f"\n{white}{repository['full_name']}{reset}") + for attr in repo_attrs: + xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") + csvLogger.logOrgRepos(repository, organization) else: xprint(response.json()) - - + + # organization events def orgEvents(): - xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() - xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="") + organization = input() + xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/orgs/{organization}/events?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") elif response.status_code == 200: for event in response.json(): - xprint(f"\n{white}{event['id']}{reset}") - xprint(f"{white}β”œβ”€ Type:{reset} {event['type']}\n{white}β”œβ”€ Created at:{reset} {event['created_at']}") - xprint(event['payload']) + xprint(f"\n{white}{event['id']}{reset}") + xprint(f"{white}β”œβ”€ Type:{reset} {event['type']}\n{white}β”œβ”€ Created at:{reset} {event['created_at']}") + xprint(event['payload']) csvLogger.logOrgEvents(event, organization) else: xprint(response.json()) - - + + # organization member def orgMember(): - xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() + xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="") + organization = input() + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() response = requests.get(f"{endpoint}/orgs/{organization}/public_members/{username}") if response.status_code == 204: xprint(f"{SignVar.positive} User ({username}) is a public member of the organization -> ({organization})") else: - xprint(f"{SignVar.negative} {response.json()['message']}") - - + xprint(f"{SignVar.negative} {response.json()['message']}") + + # Fetching user repositories def userRepos(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repositories"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repositories"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/repos?per_page={limit}") if response.status_code == 404: - xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") + xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: - for repository in response.json(): - xprint(f"\n{white}{repository['full_name']}{reset}") - for attr in repo_attrs: - xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") - csvLogger.logUserRepos(repository, username) + for repository in response.json(): + xprint(f"\n{white}{repository['full_name']}{reset}") + for attr in repo_attrs: + xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") + csvLogger.logUserRepos(repository, username) else: xprint(response.json()) - - + + # Fetching user's gists def userGists(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format('gists'), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format('gists'), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/gists?per_page={limit}") - #xprint(response.json()) + # xprint(response.json()) if response.json() == []: - xprint(f"{SignVar.negative} User does not have gists.") + xprint(f"{SignVar.negative} User does not have gists.") elif response.status_code == 404: - xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") + xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: for gist in response.json(): - xprint(f"\n{white}{gist['id']}{reset}") - for attr in gists_attrs: - xprint(f"{white}β”œβ”€ {gists_attr_dict[attr]}:{reset} {gist[attr]}") - csvLogger.logUserGists(gist) + xprint(f"\n{white}{gist['id']}{reset}") + for attr in gists_attrs: + xprint(f"{white}β”œβ”€ {gists_attr_dict[attr]}:{reset} {gist[attr]}") + csvLogger.logUserGists(gist) else: xprint(response.json()) - + # Fetching a list of organizations that a user owns or belongs to def userOrgs(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("user organizations"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("user organizations"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/orgs?per_page={limit}") if response.json() == []: xprint(f"{SignVar.negative} User ({username}) does not (belong to/own) any organizations.") elif response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: - for organization in response.json(): - print(f"\n{white}{organization['login']}{reset}") - for attr in user_orgs_attrs: - xprint(f"{white}β”œβ”€ {user_orgs_attr_dict[attr]}:{reset} {organization[attr]}") - csvLogger.logUserOrgs(organization, username) + for organization in response.json(): + print(f"\n{white}{organization['login']}{reset}") + for attr in user_orgs_attrs: + xprint(f"{white}β”œβ”€ {user_orgs_attr_dict[attr]}:{reset} {organization[attr]}") + csvLogger.logUserOrgs(organization, username) else: xprint(response.json()) - - + + # Fetching a users events def userEvents(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() - xprint(SignVar.prompt, logRoller.limitInput.format("events"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input() + xprint(SignVar.prompt, logRoller.limitInput.format("events"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/events/public?per_page={limit}") if response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: for event in response.json(): - xprint(f"\n{white}{event['id']}{reset}") - xprint(f"{white}β”œβ”€ Actor:{reset} {event['actor']['login']}") - xprint(f"{white}β”œβ”€ Type:{reset} {event['type']}") - xprint(f"{white}β”œβ”€ Repository:{reset} {event['repo']['name']}") - xprint(f"{white}β”œβ”€ Created at:{reset} {event['created_at']}") - xprint(event['payload']) - csvLogger.logUserEvents(event) + xprint(f"\n{white}{event['id']}{reset}") + xprint(f"{white}β”œβ”€ Actor:{reset} {event['actor']['login']}") + xprint(f"{white}β”œβ”€ Type:{reset} {event['type']}") + xprint(f"{white}β”œβ”€ Repository:{reset} {event['repo']['name']}") + xprint(f"{white}β”œβ”€ Created at:{reset} {event['created_at']}") + xprint(event['payload']) + csvLogger.logUserEvents(event) else: xprint(response.json()) - - + + # Fetching a target user's subscriptions def userSubscriptions(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() - xprint(SignVar.prompt, logRoller.limitInput.format("user subscriptions"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input().lower() + xprint(SignVar.prompt, logRoller.limitInput.format("user subscriptions"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/subscriptions?per_page={limit}") if response.json() == []: - xprint(f"{SignVar.negative} User does not have any subscriptions.") + xprint(f"{SignVar.negative} User does not have any subscriptions.") elif response.status_code == 404: xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: - for repository in response.json(): - xprint(f"\n{white}{repository['full_name']}{reset}") - for attr in repo_attrs: - xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") - csvLogger.logUserSubscriptions(repository, username) + for repository in response.json(): + xprint(f"\n{white}{repository['full_name']}{reset}") + for attr in repo_attrs: + xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") + csvLogger.logUserSubscriptions(repository, username) else: xprint(response.json()) - + # Fetching a list of users the target follows def userFollowing(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() - xprint(SignVar.prompt, logRoller.limitInput.format("user' following"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input().lower() + xprint(SignVar.prompt, logRoller.limitInput.format("user' following"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/following?per_page={limit}") if response.json() == []: - xprint(f"{SignVar.negative} User ({username})does not follow anyone.") + xprint(f"{SignVar.negative} User ({username})does not follow anyone.") elif response.status_code == 404: - xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") + xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: for user in response.json(): - xprint(f"\n{white}@{user['login']}{reset}") - for attr in user_attrs: - xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {user[attr]}") - csvLogger.logUserFollowing(user, username) + xprint(f"\n{white}@{user['login']}{reset}") + for attr in user_attrs: + xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {user[attr]}") + csvLogger.logUserFollowing(user, username) else: xprint(response.json()) - - -# Fetching user's followera' + + +# Fetching user's followers def userFollowers(): - xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() - xprint(SignVar.prompt, logRoller.limitInput.format("user followers"),end="");limit = int(input()) + xprint(f"{white}>> @{green}Username{reset} ", end="") + username = input().lower() + xprint(SignVar.prompt, logRoller.limitInput.format("user followers"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/users/{username}/followers?per_page={limit}") if response.json() == []: - xprint(f"{SignVar.negative} User ({username})does not have followers.") + xprint(f"{SignVar.negative} User ({username})does not have followers.") elif response.status_code == 404: - xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") + xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") elif response.status_code == 200: for follower in response.json(): - xprint(f"\n{white}@{follower['login']}{reset}") - for attr in user_attrs: - xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {follower[attr]}") - csvLogger.logUserFollowers(follower, username) + xprint(f"\n{white}@{follower['login']}{reset}") + for attr in user_attrs: + xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {follower[attr]}") + csvLogger.logUserFollowers(follower, username) else: xprint(response.json()) - - -# Checking whether or not user[A] follows user[B] + + +# Checking whether user[A] follows user[B] def userFollows(): - xprint(f"{white}>> @{green}user{white}(A) (username){reset} ", end="");user_a = input() - xprint(f"{white}>> @{green}user{white}(B) (username){reset} ", end="");user_b = input() + xprint(f"{white}>> @{green}user{white}(A) (username){reset} ", end="") + user_a = input() + xprint(f"{white}>> @{green}user{white}(B) (username){reset} ", end="") + user_b = input() response = requests.get(f"{endpoint}/users/{user_a}/following/{user_b}") if response.status_code == 204: xprint(f"{SignVar.positive} @{user_a} FOLLOWS @{user_b}") else: - xprint(f"{SignVar.negative} @{user_a} DOES NOT FOLLOW @{user_b}") - - -# User search + xprint(f"{SignVar.negative} @{user_a} DOES NOT FOLLOW @{user_b}") + + # User search + + def userSearch(): - xprint(f"{white}>> @{green}Query{white} (eg. john){reset} ", end="");query = input() - xprint(SignVar.prompt, logRoller.limitInput.format("user search"), end="");limit = int(input()) + xprint(f"{white}>> @{green}Query{white} (eg. john){reset} ", end="") + query = input() + xprint(SignVar.prompt, logRoller.limitInput.format("user search"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/search/users?q={query}&per_page={limit}").json() for user in response['items']: - xprint(f"\n{white}@{user['login']}{reset}") - for attr in user_attrs: - xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {user[attr]}") - csvLogger.logUserSearch(user, query) - - + xprint(f"\n{white}@{user['login']}{reset}") + for attr in user_attrs: + xprint(f"{white}β”œβ”€ {user_attr_dict[attr]}:{reset} {user[attr]}") + csvLogger.logUserSearch(user, query) + + # Repository search def repoSearch(): - xprint(f"{white}>> %{green}Query{white} (eg. git){reset} ", end="");query = input() - xprint(SignVar.prompt, logRoller.limitInput.format("repositor[y][ies] search"), end="");limit = int(input()) + xprint(f"{white}>> %{green}Query{white} (eg. git){reset} ", end="") + query = input() + xprint(SignVar.prompt, logRoller.limitInput.format("repositor[y][ies] search"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/search/repositories?q={query}&per_page={limit}").json() for repository in response['items']: xprint(f"\n{white}{repository['full_name']}{reset}") for attr in repo_attrs: xprint(f"{white}β”œβ”€ {repo_attr_dict[attr]}:{reset} {repository[attr]}") csvLogger.logRepoSearch(repository, query) - - + + # Topics search def topicSearch(): - xprint(f"{white}>> #{green}Query{white} (eg. osint){reset} ", end="");query = input() - xprint(SignVar.prompt, logRoller.limitInput.format("topic(s) search"),end="");limit = int(input()) + xprint(f"{white}>> #{green}Query{white} (eg. osint){reset} ", end="") + query = input() + xprint(SignVar.prompt, logRoller.limitInput.format("topic(s) search"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/search/topics?q={query}&per_page={limit}").json() for topic in response['items']: xprint(f"\n{white}{topic['name']}{reset}") for attr in topic_attrs: xprint(f"{white}β”œβ”€ {topic_attr_dict[attr]}:{reset} {topic[attr]}") csvLogger.logTopicSearch(topic, query) - - + + # Issue search def issueSearch(): - xprint(f"{white}>> !{green}Query{white} (eg. error){reset} ", end="");query = input() - xprint(SignVar.prompt, logRoller.limitInput.format("issue(s) search"), end="");limit = int(input()) + xprint(f"{white}>> !{green}Query{white} (eg. error){reset} ", end="") + query = input() + xprint(SignVar.prompt, logRoller.limitInput.format("issue(s) search"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/search/issues?q={query}&per_page={limit}").json() for issue in response['items']: xprint(f"\n\n{white}{issue['title']}{reset}") @@ -745,53 +817,53 @@ def issueSearch(): xprint(f"{white}β”œβ”€ {repo_issues_attr_dict[attr]}:{reset} {issue[attr]}") xprint(issue['body']) csvLogger.logIssueSearch(issue, query) - + # Commits search def commitsSearch(): - xprint(f"{white}>> :{green}Query{white} (eg. filename:index.php){reset} ", end="");query = input() - xprint(SignVar.prompt, logRoller.limitInput.format("commit(s) search"), end="");limit = int(input()) + xprint(f"{white}>> :{green}Query{white} (eg. filename:index.php){reset} ", end="") + query = input() + xprint(SignVar.prompt, logRoller.limitInput.format("commit(s) search"), end="") + limit = int(input()) response = requests.get(f"{endpoint}/search/commits?q={query}&per_page={limit}").json() for commit in response['items']: - xprint(f"\n{white}{commit['commit']['tree']['sha']}{reset}") - xprint(f"{white}β”œβ”€ Author:{reset} {commit['commit']['author']['name']}") - xprint(f"{white}β”œβ”€ Username:{reset} {commit['author']['login']}") - xprint(f"{white}β”œβ”€ Email:{reset} {commit['commit']['author']['email']}") - xprint(f"{white}β”œβ”€ Commiter:{reset} {commit['commit']['committer']['name']}") - xprint(f"{white}β”œβ”€ Repository:{reset} {commit['repository']['full_name']}") - xprint(f"{white}β”œβ”€ URL:{reset} {commit['html_url']}") - xprint(commit['commit']['message']) - csvLogger.logCommitsSearch(commit, query) - + xprint(f"\n{white}{commit['commit']['tree']['sha']}{reset}") + xprint(f"{white}β”œβ”€ Author:{reset} {commit['commit']['author']['name']}") + xprint(f"{white}β”œβ”€ Username:{reset} {commit['author']['login']}") + xprint(f"{white}β”œβ”€ Email:{reset} {commit['commit']['author']['email']}") + xprint(f"{white}β”œβ”€ Commiter:{reset} {commit['commit']['committer']['name']}") + xprint(f"{white}β”œβ”€ Repository:{reset} {commit['repository']['full_name']}") + xprint(f"{white}β”œβ”€ URL:{reset} {commit['html_url']}") + xprint(commit['commit']['message']) + csvLogger.logCommitsSearch(commit, query) + # View csv files def viewCsv(): logging.info(logRoller.viewingCsv) csv_files = os.listdir("output") - csv_table =Table(show_header=True, header_style=header_title) + csv_table = Table(show_header=True, header_style=header_title) csv_table.add_column("CSV", style="dim", width=12) csv_table.add_column("Size (bytes)") for csv_file in csv_files: - csv_table.add_row(str(csv_file), str(os.path.getsize("output/"+csv_file))) - xprint(csv_table) - + csv_table.add_row(str(csv_file), str(os.path.getsize("output/" + csv_file))) + xprint(csv_table) + # Read a specified csv file def readCsv(): - xprint(f"{white}>> {green}.csv {reset}(filename) ", end="");csv_file = input() + xprint(f"{white}>> {green}.csv {reset}(filename) ", end="") + csv_file = input() with open(f"output/{csv_file}.csv", "r") as file: logging.info(logRoller.readingCsv.format(csv_file)) - xprint("\n"+file.read()) + xprint("\n" + file.read()) # Delete a specified csv file def deleteCsv(): - xprint(f"{white}>> {green}.csv {reset}filename{reset} ", end="");csv_file = input() - if sys.platform.lower().startswith(("win", "darwin")): - subprocess.run(['del',f'.output\{csv_file}.csv']) - else: - subprocess.run(['sudo','rm',f'output/{csv_file}.csv'],shell=False) - + xprint(f"{white}>> {green}.csv {reset}filename{reset} ", end="") + csv_file = input() + os.remove(f'output/{csv_file}') logging.info(logRoller.deletedCsv.format(csv_file)) xprint(f"{SignVar.positive} {logRoller.deletedCsv.format(csv_file)}") @@ -800,34 +872,32 @@ def deleteCsv(): def viewLogs(): logging.info(logRoller.viewingLogs) logs = os.listdir(".logs") - logs_table =Table(show_header=True, header_style=header_title) + logs_table = Table(show_header=True, header_style=header_title) logs_table.add_column("Log", style="dim", width=12) logs_table.add_column("Size (bytes)") for log in logs: - logs_table.add_row(str(log), str(os.path.getsize(".logs/"+log))) + logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log))) xprint(logs_table) # Read a specified log file def readLog(): - xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="");log_file = input() + xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="") + log_file = input() with open(f".logs/{log_file}.log", "r") as log: logging.info(logRoller.readingLog.format(log_file)) - xprint("\n"+log.read()) - + xprint("\n" + log.read()) + # Delete a specified log file def deleteLog(): - xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="");log_file = input() - if sys.platform.lower().startswith(("win", "darwin")): - subprocess.run(['del',f'.logs\{log_file}.log']) - else: - subprocess.run(['sudo','rm',f'.logs/{log_file}.log'],shell=False) - + xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="") + log_file = input() + os.remove(f'.logs/{log_file}') logging.info(logRoller.deletedLog.format(log_file)) xprint(f"{SignVar.positive} {logRoller.deletedLog.format(log_file)}") - - + + # Downloading release tarball def downloadTarball(): logging.info(logRoller.fileDownloading.format(f"octosuite.v{version_tag}.tar")) @@ -836,7 +906,7 @@ def downloadTarball(): with open(f"downloads/octosuite.v{version_tag}.tar", "wb") as file: file.write(data.content) file.close() - + logging.info(logRoller.fileDownloaded.format(f"octosuite.v{version_tag}.tar")) xprint(SignVar.positive, logRoller.fileDownloaded.format(f"octosuite.v{version_tag}.tar")) @@ -849,24 +919,25 @@ def downloadZipball(): with open(f"downloads/octosuite.v{version_tag}.zip", "wb") as file: file.write(data.content) file.close() - + logging.info(logRoller.fileDownloaded.format(f"octosuite.v{version_tag}.zip")) xprint(SignVar.positive, logRoller.fileDownloaded.format(f"octosuite.v{version_tag}.zip")) - + def versionCheck(): response = requests.get(f"{endpoint}/repos/rly0nheart/octosuite/releases/latest") if response.json()['tag_name'] == version_tag: xprint(f"{SignVar.positive} Octosuite is up to date. Check again soon! :)") else: - xprint(f"{SignVar.info} A new release is available (octosuite.v{response.json()['tag_name']}). Exit Octosuite and run '{green_bold}pip install --upgrade octosuite{white}' to download and install the update.") - - + xprint( + f"{SignVar.info} A new release is available (octosuite.v{response.json()['tag_name']}). Exit Octosuite and run '{green_bold}pip install --upgrade octosuite{white}' to download and install the update.") + + # Author info def author(): xprint(f"{white}Richard Mwewa (Ritchie){reset}") - for key,value in author_dict.items(): - xprint(f"{white}β”œβ”€ {key}:{reset} {value}") + for key, value in author_dict.items(): + xprint(f"{white}β”œβ”€ {key}:{reset} {value}") # About program @@ -882,38 +953,37 @@ With over 20+ features, Octosuite only runs on 2 external dependencies, and retu {white_bold}Read the wiki:{reset} https://github.com/rly0nheart/octosuite/wiki {white_bold}GitHub REST API documentation:{reset} https://docs.github.com/rest """) - - + + # Close session def exitSession(): - xprint(f"{SignVar.prompt} This will close the current session, continue? (Y/n) ", end="");prompt = input().lower() + xprint(f"{SignVar.prompt} This will close the current session, continue? (Y/n) ", end="") + prompt = input().lower() if prompt == 'y': logging.info(logRoller.sessionClosed.format(datetime.now())) xprint(f"{SignVar.info} {logRoller.sessionClosed.format(datetime.now())}") exit() else: pass - + # Clear screen def clearScreen(): - ''' + """ We use 'cls' on Windows machines to clear the screen, otherwise, we use 'clear' - ''' - if sys.platform.lower().startswith(("win", "darwin")): - subprocess.run(['cls']) + """ + if sys.platform.lower().startswith("win"): + os.system('cls') else: subprocess.run(['clear'], shell=False) - - + + # Show version information def versionInfo(): - ''' - Yes... the changelog is hard coded lol - ''' - xprint(f""" + # Yes... the changelog is hard coded lol + xprint(f""" {white_bold}Whats new in v{version_tag}?{reset} -[ {green}improvement{reset} ] The Octosuite GUI (.exe/.app) is now available on GitHub - +[ {green}fixed{reset} ] [ Errno 22 ] Invalid argument (on Windows machines) +[ {green}fixed{reset} ] [ Error 2 ] The system cannot find the file specified (on Windows machines) """) diff --git a/octosuite/sign_vars.py b/octosuite/sign_vars.py index b79c9f6..dceb359 100644 --- a/octosuite/sign_vars.py +++ b/octosuite/sign_vars.py @@ -1,15 +1,14 @@ -from octosuite.colors import red, white, green, reset +from octosuite.colors import red, white, green, reset -''' -SignVar -*Even here, I couldn't think of a good name.* -The Attributes class holds the signs/symbols that show what a notification in OctoSuite might be all about. -This might not be very important or necessary in some cases, but I think it's better to know the severerity of the notifications you get in a program. -''' +""" +SignVar *Even here, I couldn't think of a good name.* The Attributes class holds the signs/symbols that show what +a notification in OctoSuite might be all about. This might not be very important or necessary in some cases, +but I think it's better to know the severity of the notifications you get in a program. +""" class SignVar: - prompt = f"{white}[{green} ? {white}]{reset}" - warning = f"{white}[{red} ! {white}]{reset}" - error = f"{white}[{red} x {white}]{reset}" - positive = f"{white}[{green} + {white}]{reset}" - negative = f"{white}[{red} - {white}]{reset}" - info = f"{white}[{green} * {white}]{reset}" + prompt = f"{white}[{green} ? {white}]{reset}" + warning = f"{white}[{red} ! {white}]{reset}" + error = f"{white}[{red} x {white}]{reset}" + positive = f"{white}[{green} + {white}]{reset}" + negative = f"{white}[{red} - {white}]{reset}" + info = f"{white}[{green} * {white}]{reset}" diff --git a/setup.py b/setup.py index dfbc0ce..792a8b0 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as file: setuptools.setup( name="octosuite", - version="2.2.2", + version="2.2.3", author="Richard Mwewa", author_email="rly0nheart@duck.com", packages=["octosuite"],