From b68842df1cd5a76689a817732ff41483833078eb Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 9 Dec 2022 23:38:36 +0200 Subject: [PATCH 01/51] Update octosuite.py --- octosuite/octosuite.py | 1120 ++++++++++++++++++++-------------------- 1 file changed, 558 insertions(+), 562 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index d01d37d..c9628d9 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -1,6 +1,6 @@ #!usr/bin/python -import os +import os import sys import shutil import logging @@ -15,23 +15,199 @@ from pyreadline3 import Readline from rich import print as xprint from octosuite.banners import version_tag, ascii_banner from octosuite.colors import red, white, green, header_title, reset -from octosuite.message_prefixes import PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol +from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) -from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv -from octosuite.log_roller import ctrl_c, error, session_opened, session_closed, viewing_logs, viewing_csv, deleted_log, reading_log, reading_csv, deleted_csv, file_downloading, file_downloaded, info_not_found, user_not_found, org_not_found, repo_or_user_not_found, limit_output -from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_profile, log_repo_path_contents, log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, log_org_profile, log_user_repos, log_user_gists, log_user_orgs, log_user_events, log_user_subscriptions, log_user_following, log_user_followers, log_repos_search, log_users_search, log_topics_search, log_issues_search, log_commits_search # log_org_events +from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ + logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv +from octosuite.log_roller import ctrl_c, error, session_opened, session_closed, viewing_logs, viewing_csv, \ + deleted_log, reading_log, reading_csv, deleted_csv, file_downloading, file_downloaded, info_not_found, \ + user_not_found, org_not_found, repo_or_user_not_found, limit_output +from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_profile, log_repo_path_contents, \ + log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, \ + log_org_profile, log_user_repos, log_user_gists, log_user_orgs, log_user_events, log_user_subscriptions, \ + log_user_following, log_user_followers, log_repos_search, log_users_search, log_topics_search, log_issues_search, \ + log_commits_search # log_org_events readline = Readline() +""" +path_finder() +This method is responsible for creating/checking the availability of the (.logs, output, downloads) folders, +enabling logging to automatically log network/user activity to a file, + and logging the start of a session. +""" + + +def path_finder(): + """ + Check 3 directories (.logs, output, downloads) on startup + If they exist, ignore, otherwise, create them + """ + directory_list = ['.logs', 'output', 'downloads'] + for directory in directory_list: + os.makedirs(directory, exist_ok=True) + + +""" +Configure logging and check for updates +""" + + +def configure_logging(): + """ + 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) + # Log the start of a session + logging.info(session_opened.format(platform.node(), getpass.getuser())) + + +def check_updates(): + response = requests.get("https://api.github.com/repos/bellingcat/octosuite/releases/latest").json() + if response['tag_name'] == version_tag: + """ + Ignore if the program is up to date + """ + pass + else: + xprint( + f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") + + +def delete_csv(): + xprint(f"{green}csv {white}(filename):{reset} ", end="") + csv_file = input() + os.remove(os.path.join("output", csv_file)) + logging.info(deleted_csv.format(csv_file)) + xprint(f"{POSITIVE} {deleted_csv.format(csv_file)}") + + +def clear_csv(): + xprint(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue? (yes/no) ", end="") + prompt = input().lower() + if prompt == "yes": + shutil.rmtree("output", ignore_errors=True) + xprint(f"{INFO} CSV files cleared successfully!") + else: + pass + + +def view_logs(): + logging.info(viewing_logs) + logs = os.listdir(".logs") + logs_table = Table(show_header=True, header_style=header_title) + logs_table.add_column("Log", style="dim") + logs_table.add_column("Size (bytes)") + for log in logs: + logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log))) + xprint(logs_table) + + +def read_log(): + xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") + log_file = input() + with open(os.path.join(".logs", log_file + ".log"), "r") as log: + logging.info(reading_log.format(log_file)) + xprint("\n" + log.read()) + + +def delete_log(): + xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") + log_file = input() + os.remove(os.path.join(".logs", log_file)) + logging.info(deleted_log.format(log_file)) + xprint(f"{POSITIVE} {deleted_log.format(log_file)}") + + +def clear_logs(): + xprint( + f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", + end="") + prompt = input().lower() + if prompt == "yes": + shutil.rmtree(".logs", ignore_errors=True) + xprint(f"{INFO} Logs cleared successfully!") + xprint(f"{INFO} {session_closed.format(datetime.now())}") + exit() + else: + pass + + +def exit_session(): + xprint(f"{PROMPT} This will close the current session, continue? (yes/no) ", end="") + prompt = input().lower() + if prompt == "yes": + logging.info(session_closed.format(datetime.now())) + xprint(f"{INFO} {session_closed.format(datetime.now())}") + exit() + else: + pass + + +def clear_screen(): + """ + using 'cls' on Windows machines to clear the screen, + otherwise, use 'clear' + """ + os.system('cls' if os.name == 'nt' else 'clear') + + +def view_csv(): + logging.info(viewing_csv) + csv_files = os.listdir("output") + csv_table = Table(show_header=True, header_style=header_title) + csv_table.add_column("CSV", style="dim") + 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) + + +def read_csv(): + xprint(f"{green}csv {white}(filename):{reset} ", end="") + csv_file = input() + with open(os.path.join("output", csv_file + ".csv"), "r") as file: + logging.info(reading_csv.format(csv_file)) + text = Text(file.read()) + xprint(text) + + +def about(): + about_text = f""" + OCTOSUITE © 2023 Richard Mwewa + +An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations. +With over 20+ features, Octosuite only runs on 2 external dependencies, and returns the gathered intelligence in a highly readable format. + + +Whats new in v{version_tag}? +[{green}IMPROVED{reset}] Users will now be able to view previous commands with the ↑ ↓ arrow keys +[{green}IMPROVED{reset}] Removed width from tables, so that they can auto adjust +[{green}ADDED{reset}] Added the 'log:clear' command, which will be used to clear all logs +[{green}ADDED{reset}] Added the 'csv:clear' command, which will be used to clear all csv files +[{green}GUI{reset}] GUI app for OSX is now available +[{green}GUI{reset}] The GUI will now come as a standalone executable +[{green}GUI{reset}] Added an option to check for updates + +Read the wiki: https://github.com/bellingcat/octosuite/wiki +GitHub REST API documentation: https://docs.github.com/rest +""" + xprint(about_text) + + class Octosuite: - def __init__(self): + def __init__(self, args): + self.args = args # API endpoint self.endpoint = 'https://api.github.com' - + # A list of tuples mapping commands to their methods - self.command_map = [("exit", self.exit_session), - ("clear", self.clear_screen), - ("about", self.about), + self.command_map = [("exit", exit_session), + ("clear", clear_screen), + ("about", about), ("author", self.author), ("help", help_command), ("help:source", source_command), @@ -74,298 +250,226 @@ class Octosuite: ("search:issues", self.issues_search), ("search:commits", self.commits_search), ("logs", logs), - ("logs:view", self.view_logs), - ("logs:read", self.read_log), - ("logs:delete", self.delete_log), - ("logs:clear", self.clear_logs), + ("logs:view", view_logs), + ("logs:read", read_log), + ("logs:delete", delete_log), + ("logs:clear", clear_logs), ("csv", csv), - ("csv:view", self.view_csv), - ("csv:read", self.read_csv), - ("csv:delete", self.delete_csv), - ("csv:clear", self.clear_csv)] + ("csv:view", view_csv), + ("csv:read", read_csv), + ("csv:delete", delete_csv), + ("csv:clear", clear_csv)] # 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'} - - + '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'] + 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_repos': 'Repositories', - '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'} - - + '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_repos': 'Repositories', + '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', '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'] + self.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 self.repo_attr_dict = {'id': 'ID', - 'description': 'About', - 'forks': 'Forks', - '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', - 'fork': 'Is fork?', - 'allow_forking': 'Is forkable?', - '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'} - - + 'description': 'About', + 'forks': 'Forks', + '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', + 'fork': 'Is fork?', + 'allow_forking': 'Is forkable?', + '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'} + # Repo releases attributes - self.repo_releases_attrs = ['id', 'node_id', 'tag_name', 'target_commitish', 'assets', 'draft', 'prerelease', 'created_at', - 'published_at'] + self.repo_releases_attrs = ['id', 'node_id', 'tag_name', 'target_commitish', 'assets', 'draft', 'prerelease', + 'created_at', + 'published_at'] # Repo releases attribute dictionary self.repo_releases_attr_dict = {'id': 'ID', - 'node_id': 'Node ID', - 'tag_name': 'Tag', - 'target_commitish': 'Branch', - 'assets': 'Assets', - 'draft': 'Is draft?', - 'prerelease': 'Is prerelease?', - 'created_at': 'Created at', - 'published_at': 'Published at'} - - + 'node_id': 'Node ID', + 'tag_name': 'Tag', + 'target_commitish': 'Branch', + 'assets': 'Assets', + 'draft': 'Is draft?', + 'prerelease': 'Is prerelease?', + 'created_at': 'Created at', + 'published_at': 'Published 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'] + 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'} - - + '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 + 'id': 'ID', + 'node_id': 'Node ID', + 'gravatar_id': 'Gravatar ID', + 'site_admin': 'Is site admin?', + 'type': 'Account type', + 'html_url': 'URL'} + + # Topic attributes 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'} - - + 'curated': 'Curated', + 'featured': 'Featured', + 'display_name': 'Display name', + 'created_by': 'Created by', + 'created_at': 'Created at', + 'updated_at': 'Updated at'} + # Gists attribute - self.gists_attrs = ['node_id', 'description', 'comments', 'files', 'git_push_url', 'public', 'truncated', 'updated_at'] + 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'} - - + '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'] + self.issue_attrs = ['id', 'node_id', 'score', 'state', 'number', 'comments', 'milestone', 'assignee', + 'assignees', 'labels', + 'locked', 'draft', 'closed_at'] # 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'} - + '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'} + # Repo issues attributes self.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'] + 'active_lock_reason', 'author_association', 'assignees', 'labels', 'locked', + 'closed_at', + 'created_at', 'updated_at'] # Issue attribute dict self.repo_issues_attr_dict = {'id': 'ID', - 'node_id': 'Node ID', - 'number': 'Number', - 'state': 'State', - 'reactions': 'Reactions', - 'comments': 'Comments', - 'milestone': 'Milestone', - 'assignee': 'Assignee', - 'assignees': 'Assignees', - 'author_association': 'Author association', - 'labels': 'Labels', - 'locked': 'Is locked?', - 'active_lock_reason': 'Lock reason', - 'closed_at': 'Closed at', - 'created_at': 'Created at', - 'updated_at': 'Updated at'} - - + 'node_id': 'Node ID', + 'number': 'Number', + 'state': 'State', + 'reactions': 'Reactions', + 'comments': 'Comments', + 'milestone': 'Milestone', + 'assignee': 'Assignee', + 'assignees': 'Assignees', + 'author_association': 'Author association', + 'labels': 'Labels', + 'locked': 'Is locked?', + 'active_lock_reason': 'Lock reason', + 'closed_at': 'Closed at', + 'created_at': 'Created at', + 'updated_at': 'Updated at'} + # User organizations attributes self.user_orgs_attrs = ['avatar_url', 'id', 'node_id', 'url', 'description'] self.user_orgs_attr_dict = {'avatar_url': 'Profile Photo', - 'id': 'ID', - 'node_id': 'Node ID', - 'url': 'URL', - 'description': 'About'} - + 'id': 'ID', + 'node_id': 'Node ID', + 'url': 'URL', + 'description': 'About'} + # Author dictionary self.author_dict = {'Alias': 'rly0nheart', - 'Country': ':zambia: Zambia, Africa', - 'About.me': 'https://about.me/rly0nheart', - 'Buy Me A Coffee': 'https://buymeacoffee.com/189381184'} - - - """ - path_finder() - This method is responsible for creating/checking the availability of the (.logs, output, downloads) folders, - enabling logging to automatically log network/user activity to a file, - and logging the start of a session. - """ - def path_finder(self): - """ - Check 3 directories (.logs, output, downloads) on startup - If they exist, ignore, otherwise, create them - """ - directory_list = ['.logs', 'output', 'downloads'] - for directory in directory_list: - os.makedirs(directory, exist_ok=True) - - - """ - Configure logging and check for updates - """ - def configure_logging(self): - """ - 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) - # Log the start of a session - logging.info(session_opened.format(platform.node(), getpass.getuser())) - - - # Check for updates - def check_updates(self): - response = requests.get("https://api.github.com/repos/bellingcat/octosuite/releases/latest").json() - if response['tag_name'] == version_tag: - """ - Ignore if the program is up to date - """ - pass - else: - xprint(f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") - - - """ - on_start() - This is the main method, responsible for mapping commands, calling other methods, and catching exceptions - """ - def on_start(self): - self.path_finder() - self.clear_screen() - self.configure_logging() - self.check_updates() - xprint(ascii_banner()[1], ascii_banner()[0]) - - """ - Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) - or if the 'exit' command is entered. - """ - while True: - xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",end="") - command_input = input().lower() - print("\n") - """ - Iterate over the command_map and check if the user input matches any command in it [command_map], - if there's a match, we return its method. If no match is found, we ignore it. - """ - for command, method in self.command_map: - if command_input == command: - method() - print("\n") - else: - pass - - + 'Country': ':zambia: Zambia, Africa', + 'About.me': 'https://about.me/rly0nheart', + 'Buy Me A Coffee': 'https://buymeacoffee.com/189381184'} + # Fetching organization info def org_profile(self): - xprint(f"{white}@{green}Organization {white}(username):{reset} ", end="") - organization = input() + if self.args.organization: + organization = self.args.organization + else: + organization = input("@Organization: ") response = requests.get(f"{self.endpoint}/orgs/{organization}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -377,12 +481,13 @@ class Octosuite: log_org_profile(response) else: xprint(response.json()) - - + # Fetching user information def user_profile(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() + if self.args.username: + username = self.args.username + else: + username = input("@Username: ") response = requests.get(f"{self.endpoint}/users/{username}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -394,15 +499,16 @@ class Octosuite: log_user_profile(response) else: xprint(response.json()) - - + # Fetching repository information def repo_profile(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username) ", end="") - username = input() - response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}") + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + response = requests.get(f"{self.endpoint}/repos/{self.username}/{repo_name}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") elif response.status_code == 200: @@ -413,16 +519,17 @@ class Octosuite: log_repo_profile(response) else: xprint(response.json()) - - + # Get path contents def path_contents(self): - 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() + if self.args.repository and self.args.username and self.args.path_name: + repo_name = self.args.repository + username = self.args.username + path_name = self.args.path_name + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + path_name = input("~/path/name ") response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/contents/{path_name}") if response.status_code == 404: xprint(f"{NEGATIVE} {info_not_found.format(repo_name, username, path_name)}") @@ -433,19 +540,20 @@ class Octosuite: path_contents_tree.add(f"{self.path_attr_dict[attr]}: {content[attr]}") xprint(path_contents_tree) log_repo_path_contents(content, repo_name) - xprint(INFO, f"Found {content_count} file(s) in {repo_name}/{path_name}.") + xprint(INFO, f"Found {content_count} file(s) in {repo_name}/{path_name}.") else: xprint(response.json()) - - + # repo contributors def repo_contributors(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username) ", end="") - username = input() - xprint(PROMPT, limit_output.format("contributors"), end="") - limit = int(input()) + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + limit = self.args.limit + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + limit = input(limit_output.format("contributors")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -458,16 +566,17 @@ class Octosuite: log_repo_contributors(contributor, repo_name) else: xprint(response.json()) - - + # repo stargazers def repo_stargazers(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("repository stargazers"), end="") - limit = int(input()) + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + limit = self.args.limit + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + limit = input(limit_output.format("stargazers")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -482,16 +591,17 @@ class Octosuite: log_repo_stargazers(stargazer, repo_name) else: xprint(response.json()) - - + # repo forks def repo_forks(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}@{green}Owner{white} (username):{reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("repository forks"), end="") - limit = int(input()) + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + limit = self.args.limit + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + limit = input(limit_output.format("forks")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -506,19 +616,21 @@ class Octosuite: log_repo_forks(fork, count) else: xprint(response.json()) - + # Repo issues def repo_issues(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("repository issues"), end="") - limit = int(input()) + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + limit = self.args.limit + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + limit = input(limit_output.format("issues")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") - elif response.json() == []: + elif not response.json(): xprint(f"{NEGATIVE} Repository does not have open issues -> ({repo_name})") elif response.status_code == 200: for issue in response.json(): @@ -530,20 +642,21 @@ class Octosuite: log_repo_issues(issue, repo_name) else: xprint(response.json()) - - + # Repo releases def repo_releases(self): - xprint(f"{white}>> %{green}Repository{reset} ", end="") - repo_name = input() - xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("repository releases"), end="") - limit = int(input()) + if self.args.repository and self.args.username and self.args.limit: + repo_name = self.args.repository + username = self.args.username + limit = self.args.limit + else: + repo_name = input("%Repository: ") + username = input("@Username: ") + limit = input(limit_output.format("repository releases")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") - elif response.json() == []: + elif not response.json(): xprint(f"{NEGATIVE} Repository does not have releases -> ({repo_name})") elif response.status_code == 200: for release in response.json(): @@ -555,14 +668,15 @@ class Octosuite: log_repo_releases(release, repo_name) else: xprint(response.json()) - - + # Fetching organization repositories def org_repos(self): - xprint(f"{white}@{green}organization{white} (username):{reset} ", end="") - organization = input() - xprint(PROMPT, limit_output.format("organization repositories"), end="") - limit = int(input()) + if self.args.organization and self.args.limit: + organization = self.args.organization + limit = self.args.limit + else: + organization = input("@Organization: ") + limit = input(limit_output.format("organization repositories")) response = requests.get(f"{self.endpoint}/orgs/{organization}/repos?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -575,14 +689,15 @@ class Octosuite: log_org_repos(repository, organization) else: xprint(response.json()) - - + # organization events def org_events(self): - xprint(f"{white}@{green}organization{white} (username):{reset} ", end="") - organization = input() - xprint(PROMPT, limit_output.format("organization repositories"), end="") - limit = int(input()) + if self.args.organization and self.args.limit: + organization = self.args.organization + limit = self.args.limit + else: + organization = input("@Organization: ") + limit = input(limit_output.format("organization events")) response = requests.get(f"{self.endpoint}/orgs/{organization}/events?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -596,27 +711,29 @@ class Octosuite: # log_org_events(event, organization) else: xprint(response.json()) - - + # organization member def org_member(self): - xprint(f"{white}@{green}organization{white} (username):{reset} ", end="") - organization = input() - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() + if self.args.organization and self.args.username: + organization = self.args.organization + username = self.args.username + else: + organization = input("@Organization: ") + username = input("@Username: ") response = requests.get(f"{self.endpoint}/orgs/{organization}/public_members/{username}") if response.status_code == 204: xprint(f"{POSITIVE} User ({username}) is a public member of the organization -> ({organization})") else: xprint(f"{NEGATIVE} {response.json()['message']}") - - + # Fetching user repositories def user_repos(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("repositories"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("repositories")) response = requests.get(f"{self.endpoint}/users/{username}/repos?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -629,16 +746,17 @@ class Octosuite: log_user_repos(repository, username) else: xprint(response.json()) - - + # Fetching user's gists def user_gists(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format('gists'), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format('gists')) response = requests.get(f"{self.endpoint}/users/{username}/gists?per_page={limit}") - if response.json() == []: + if not response.json(): xprint(f"{NEGATIVE} User does not have gists.") elif response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -651,16 +769,17 @@ class Octosuite: log_user_gists(gist) else: xprint(response.json()) - - + # Fetching a list of organizations that a user owns or belongs to def user_orgs(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("user organizations"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("user organizations")) response = requests.get(f"{self.endpoint}/users/{username}/orgs?per_page={limit}") - if response.json() == []: + if not response.json(): xprint(f"{NEGATIVE} User ({username}) does not (belong to/own) any organizations.") elif response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -673,14 +792,15 @@ class Octosuite: log_user_orgs(organization, username) else: xprint(response.json()) - - + # Fetching a users events def user_events(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input() - xprint(PROMPT, limit_output.format("events"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("events")) response = requests.get(f"{self.endpoint}/users/{username}/events/public?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -696,38 +816,40 @@ class Octosuite: log_user_events(event) else: xprint(response.json()) - - + # Fetching a target user's subscriptions def user_subscriptions(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input().lower() - xprint(PROMPT, limit_output.format("user subscriptions"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("user subscriptions")) response = requests.get(f"{self.endpoint}/users/{username}/subscriptions?per_page={limit}") - if response.json() == []: + if not response.json(): xprint(f"{NEGATIVE} User does not have any subscriptions.") elif response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") elif response.status_code == 200: for repository in response.json(): - subscriptions_tree =Tree("\n" + repository['full_name']) + subscriptions_tree = Tree("\n" + repository['full_name']) for attr in self.repo_attrs: subscriptions_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(subscriptions_tree) log_user_subscriptions(repository, username) else: xprint(response.json()) - - + # Fetching a list of users the target follows def user_following(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input().lower() - xprint(PROMPT, limit_output.format("user' following"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("user' following")) response = requests.get(f"{self.endpoint}/users/{username}/following?per_page={limit}") - if response.json() == []: + if not response.json(): xprint(f"{NEGATIVE} User ({username})does not follow anyone.") elif response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -740,16 +862,17 @@ class Octosuite: log_user_following(user, username) else: xprint(response.json()) - - + # Fetching user's followers def user_followers(self): - xprint(f"{white}@{green}username:{reset} ", end="") - username = input().lower() - xprint(PROMPT, limit_output.format("user followers"), end="") - limit = int(input()) + if self.args.username and self.args.limit: + username = self.args.username + limit = self.args.limit + else: + username = input("@Username: ") + limit = input(limit_output.format("user followers")) response = requests.get(f"{self.endpoint}/users/{username}/followers?per_page={limit}") - if response.json() == []: + if not response.json(): xprint(f"{NEGATIVE} User ({username})does not have followers.") elif response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -762,27 +885,29 @@ class Octosuite: log_user_followers(follower, username) else: xprint(response.json()) - - + # Checking whether user[A] follows user[B] def user_follows(self): - 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() + if self.args.username and self.args.username_b: + user_a = self.args.username + user_b = self.args.username_b + else: + user_a = input("@User_A: ") + user_b = input("@User_B") response = requests.get(f"{self.endpoint}/users/{user_a}/following/{user_b}") if response.status_code == 204: xprint(f"{POSITIVE} @{user_a} FOLLOWS @{user_b}") else: xprint(f"{NEGATIVE} @{user_a} DOES NOT FOLLOW @{user_b}") - - + # User search def users_search(self): - xprint(f"{white}@{green}query{white} (eg. john):{reset} ", end="") - query = input() - xprint(PROMPT, limit_output.format("user search"), end="") - limit = int(input()) + if self.args.query and self.args.limit and self.args.limit: + query = self.args.query + limit = self.args.limit + else: + query = input("@Username (search): ") + limit = input(limit_output.format("user search")) response = requests.get(f"{self.endpoint}/search/users?q={query}&per_page={limit}").json() for user in response['items']: users_search_tree = Tree("\n" + user['login']) @@ -790,14 +915,15 @@ class Octosuite: users_search_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(users_search_tree) log_users_search(user, query) - - + # Repository search def repos_search(self): - xprint(f"{white}%{green}query{white} (eg. git):{reset} ", end="") - query = input() - xprint(PROMPT, limit_output.format("repositor[y][ies] search"), end="") - limit = int(input()) + if self.args.query and self.args.limit: + query = self.args.query + limit = self.args.limit + else: + query = input("%Repository (search): ") + limit = input(limit_output.format("repositor[y][ies] search")) response = requests.get(f"{self.endpoint}/search/repositories?q={query}&per_page={limit}").json() for repository in response['items']: repos_search_tree = Tree("\n" + repository['full_name']) @@ -805,14 +931,15 @@ class Octosuite: repos_search_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_search_tree) log_repos_search(repository, query) - - + # Topics search def topics_search(self): - xprint(f"{white}#{green}query{white} (eg. osint):{reset} ", end="") - query = input() - xprint(PROMPT, limit_output.format("topic(s) search"), end="") - limit = int(input()) + if self.args.query and self.args.limit: + query = self.args.query + limit = self.args.limit + else: + query = input(":Topic (search): ") + limit = input(limit_output.format("topic(s) search")) response = requests.get(f"{self.endpoint}/search/topics?q={query}&per_page={limit}").json() for topic in response['items']: topics_search_tree = Tree("\n" + topic['name']) @@ -820,14 +947,15 @@ class Octosuite: topics_search_tree.add(f"{self.topic_attr_dict[attr]}: {topic[attr]}") xprint(topics_search_tree) log_topics_search(topic, query) - - + # Issue search def issues_search(self): - xprint(f"{white}!{green}Query{white} (eg. error):{reset} ", end="") - query = input() - xprint(PROMPT, limit_output.format("issue(s) search"), end="") - limit = int(input()) + if self.args.query and self.args.limit: + query = self.args.query + limit = self.args.limit + else: + query = input("!Issue (search): ") + limit = input(limit_output.format("issue(s) search")) response = requests.get(f"{self.endpoint}/search/issues?q={query}&per_page={limit}").json() for issue in response['items']: issues_search_tree = Tree("\n" + issue['title']) @@ -836,14 +964,15 @@ class Octosuite: xprint(issues_search_tree) xprint(issue['body']) log_issues_search(issue, query) - - + # Commits search def commits_search(self): - xprint(f"{white}:{green}Query{white} (eg. filename:index.php):{reset} ", end="") - query = input() - xprint(PROMPT, limit_output.format("commit(s) search"), end="") - limit = int(input()) + if self.args.query and self.args.limit: + query = self.args.query + limit = self.args.limit + else: + query = input(";Commit (search): ") + limit = input(limit_output.format("commit(s) search")) response = requests.get(f"{self.endpoint}/search/commits?q={query}&per_page={limit}").json() for commit in response['items']: commits_search_tree = Tree("\n" + commit['commit']['tree']['sha']) @@ -856,93 +985,7 @@ class Octosuite: xprint(commits_search_tree) xprint(commit['commit']['message']) log_commits_search(commit, query) - - - # View csv files - def view_csv(self): - logging.info(viewing_csv) - csv_files = os.listdir("output") - csv_table = Table(show_header=True, header_style=header_title) - csv_table.add_column("CSV", style="dim") - 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) - - - # Read a specified csv file - def read_csv(self): - xprint(f"{green}csv {white}(filename):{reset} ", end="") - csv_file = input() - with open(os.path.join("output", csv_file + ".csv"), "r") as file: - logging.info(reading_csv.format(csv_file)) - text = Text(file.read()) - xprint(text) - - - # Delete a specified csv file - def delete_csv(self): - xprint(f"{green}csv {white}(filename):{reset} ", end="") - csv_file = input() - os.remove(os.path.join("output", csv_file)) - logging.info(deleted_csv.format(csv_file)) - xprint(f"{POSITIVE} {deleted_csv.format(csv_file)}") - - - # Clear csv - def clear_csv(self): - xprint(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue? (yes/no) ", end="") - prompt = input().lower() - if prompt == "yes": - shutil.rmtree("output", ignore_errors=True) - xprint(f"{INFO} CSV files cleared successfully!") - else: - pass - - - # View octosuite log files - def view_logs(self): - logging.info(viewing_logs) - logs = os.listdir(".logs") - logs_table = Table(show_header=True, header_style=header_title) - logs_table.add_column("Log", style="dim") - logs_table.add_column("Size (bytes)") - for log in logs: - logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log))) - xprint(logs_table) - - - # Read a specified log file - def read_log(self): - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() - with open(os.path.join(".logs", log_file + ".log"), "r") as log: - logging.info(reading_log.format(log_file)) - xprint("\n" + log.read()) - - - # Delete a specified log file - def delete_log(self): - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() - os.remove(os.path.join(".logs", log_file)) - logging.info(deleted_log.format(log_file)) - xprint(f"{POSITIVE} {deleted_log.format(log_file)}") - - - # Clear logs - def clear_logs(self): - xprint(f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", end="") - prompt = input().lower() - if prompt == "yes": - shutil.rmtree(".logs", ignore_errors=True) - xprint(f"{INFO} Logs cleared successfully!") - xprint(f"{INFO} {session_closed.format(datetime.now())}") - exit() - else: - pass - - + # Downloading release tarball def download_tarball(self): logging.info(file_downloading.format(f"octosuite.v{version_tag}.tar")) @@ -951,11 +994,10 @@ class Octosuite: with open(os.path.join("downloads", f"octosuite.v{version_tag}.tar"), "wb") as file: file.write(data.content) file.close() - + logging.info(file_downloaded.format(f"octosuite.v{version_tag}.tar")) xprint(POSITIVE, file_downloaded.format(f"octosuite.v{version_tag}.tar")) - - + # Downloading release zipball def download_zipball(self): logging.info(file_downloading.format(f"octosuite.v{version_tag}.zip")) @@ -964,59 +1006,13 @@ class Octosuite: with open(os.path.join("downloads", f"octosuite.v{version_tag}.zip"), "wb") as file: file.write(data.content) file.close() - + logging.info(file_downloaded.format(f"octosuite.v{version_tag}.zip")) xprint(POSITIVE, file_downloaded.format(f"octosuite.v{version_tag}.zip")) - - + # Author info def author(self): author_tree = Tree(f"{white}Richard Mwewa (Ritchie){reset}") - for author_key, author_value in self.author_dict.items(self): + for author_key, author_value in self.author_dict.items(): author_tree.add(f"{white}{author_key}:{reset} {author_value}") xprint(author_tree) - - - # About program - def about(self): - about_text = f""" - OCTOSUITE © 2023 Richard Mwewa - -An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations. -With over 20+ features, Octosuite only runs on 2 external dependencies, and returns the gathered intelligence in a highly readable format. - - -Whats new in v{version_tag}? -[{green}IMPROVED{reset}] Users will now be able to view previous commands with the ↑ ↓ arrow keys -[{green}IMPROVED{reset}] Removed width from tables, so that they can auto adjust -[{green}ADDED{reset}] Added the 'log:clear' command, which will be used to clear all logs -[{green}ADDED{reset}] Added the 'csv:clear' command, which will be used to clear all csv files -[{green}GUI{reset}] The GUI will now come as a standalone executable -[{green}GUI{reset}] Added an option to check for updates - -Read the wiki: https://github.com/bellingcat/octosuite/wiki -GitHub REST API documentation: https://docs.github.com/rest -""" - xprint(about_text) - - - # Close session - def exit_session(self): - xprint(f"{PROMPT} This will close the current session, continue? (yes/no) ", end="") - prompt = input().lower() - if prompt == "yes": - logging.info(session_closed.format(datetime.now())) - xprint(f"{INFO} {session_closed.format(datetime.now())}") - exit() - else: - pass - - - # Clear screen - def clear_screen(self): - """ - using 'cls' on Windows machines to clear the screen, - otherwise, use 'clear' - """ - os.system('cls' if os.name == 'nt' else 'clear') - From beb8d561f8ca83dc12f76286f5284b295b8acce4 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 9 Dec 2022 23:40:18 +0200 Subject: [PATCH 02/51] Update main.py --- octosuite/main.py | 137 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 3 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index 45a0361..1aab2bf 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -1,11 +1,142 @@ # import everything from the octosuite.py file -from octosuite.octosuite import * # I drifted away from the 'pythonic way' here +import argparse +from octosuite.octosuite import * # I drifted away from the 'pythonic way' here + + +def usage(): + return """ + Basic usage + ----------- + # Get user profile info + octosuite --module user_profile --username + + # Get organization profile info + octosuite --module org_profile --organization + + # Get repo profile info + octosuite --module repo_profile --username --repository + + + + Searching + --------- + # Search users + octosuite --module users_search --query + + # Search issues + octosuite --module issues_search --query + + # Search commits + octosuite --module commits_search --query + + # Search topics + octosuite --module topics_search --query + + # Search repositories + octosuite --module repos_search --query + """ + + +def create_parser(): + parser = argparse.ArgumentParser(description='OCTOSUITE: Advanced GitHub osint framework — by Richard Mwewa | https://about.me/rly0nheart', usage=usage()) + parser.add_argument('-m', '--module', help='module', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', + 'user_subscriptions', 'user_following', 'user_followers', 'user_follows', + 'org_profile', 'org_repos', 'org_events', 'org_member', + 'repo_profile', 'repo_contributors', 'repo_stargazers', 'repo_forks', + 'repo_issues', 'repo_releases', 'repo_path_contents', 'users_search', 'issues_search', + 'commits_search', 'topics_search', 'repos_search']) + parser.add_argument('-u', '--username', help='username') + parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)') + parser.add_argument('-org', '--organization', help='organization name') + parser.add_argument('-repo', '--repository', help='repository name') + parser.add_argument('-pn', '--path_name', help='path name (used with repo_path_contents)') + parser.add_argument('-q', '--query', help='query (used with search modules)') + parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) + parser.add_argument('--colors', help='enable or disable colors (default: %(default)s)', type=bool, default=True) + return parser + + +parser = create_parser() +args = parser.parse_args() def octosuite(): try: - run = Octosuite() - run.on_start() + run = Octosuite(args) + path_finder() + clear_screen() + configure_logging() + check_updates() + xprint(ascii_banner()[1], ascii_banner()[0]) + if args.module == "user_profile": + run.user_profile() + elif args.module == "user_repos": + run.user_repos() + elif args.module == "user_gists": + run.user_gists() + elif args.module == "user_orgs": + run.user_orgs() + elif args.module == "user_events": + run.user_events() + elif args.module == "user_subscriptions": + run.user_subscriptions() + elif args.module == "user_following": + run.user_following() + elif args.module == "user_followers": + run.user_followers() + elif args.module == "user_follows": + run.user_follows() + elif args.module == "users_search": + run.users_search() + elif args.module == "issues_search": + run.issues_search() + elif args.module == "commits_search": + run.commits_search() + elif args.module == "topics_search": + run.topics_search() + elif args.module == "repos_search": + run.repos_search() + elif args.module == "org_profile": + run.org_profile() + elif args.module == "org_repos": + run.org_repos() + elif args.module == "org_events": + run.org_events() + elif args.module == "org_member": + run.org_member() + elif args.module == "repo_profile": + run.repo_profile() + elif args.module == "repo_contributors": + run.repo_contributors() + elif args.module == "repo_stargazers": + run.repo_stargazers() + elif args.module == "repo_forks": + run.repo_forks() + elif args.module == "repo_issues": + run.repo_issues() + elif args.module == "repo_releases": + run.repo_releases() + elif args.module == "repo_path_contents": + run.path_contents() + else: + """ + Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) + or if the 'exit' command is entered. + """ + while True: + xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",end="") + command_input = input().lower() + print("\n") + """ + Iterate over the command_map and check if the user input matches any command in it [command_map], + if there's a match, we return its method. If no match is found, we ignore it. + """ + for command, method in run.command_map: + if command_input == command: + method() + print("\n") + else: + pass except KeyboardInterrupt: logging.warning(ctrl_c) From 283a92fa8410fa6cd2f29afe0165003eae691db9 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Mon, 12 Dec 2022 14:57:36 +0200 Subject: [PATCH 03/51] Update octosuite.py --- octosuite/octosuite.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index c9628d9..09b2ff8 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -11,11 +11,11 @@ from rich.text import Text from rich.tree import Tree from rich.table import Table from datetime import datetime -from pyreadline3 import Readline +# from pyreadline3 import Readline from rich import print as xprint -from octosuite.banners import version_tag, ascii_banner -from octosuite.colors import red, white, green, header_title, reset -from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol +from banners import version_tag, ascii_banner +from colors import red, white, green, header_title, reset +from message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv @@ -28,7 +28,7 @@ from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_pr log_user_following, log_user_followers, log_repos_search, log_users_search, log_topics_search, log_issues_search, \ log_commits_search # log_org_events -readline = Readline() +# readline = Readline() """ path_finder() From a89596efc280d9ee0a0faffceaafb005d3621911 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:00:56 +0200 Subject: [PATCH 04/51] Update octosuite.py --- octosuite/octosuite.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 09b2ff8..a49820f 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -13,9 +13,9 @@ from rich.table import Table from datetime import datetime # from pyreadline3 import Readline from rich import print as xprint -from banners import version_tag, ascii_banner -from colors import red, white, green, header_title, reset -from message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol +from octosuite.banners import version_tag, ascii_banner +from octosuite.colors import red, white, green, header_title, reset +from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv From e3c151e2d3bc1762b89106f34ffa14ab6f777b2a Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:03:53 +0200 Subject: [PATCH 05/51] Update and rename colors.py to config.py --- octosuite/colors.py | 51 -------------------- octosuite/config.py | 111 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 51 deletions(-) delete mode 100644 octosuite/colors.py create mode 100644 octosuite/config.py diff --git a/octosuite/colors.py b/octosuite/colors.py deleted file mode 100644 index c6188f9..0000000 --- a/octosuite/colors.py +++ /dev/null @@ -1,51 +0,0 @@ -import psutil -import platform -from rich.tree import Tree -from datetime import datetime -from rich import print as xprint - - -# This file is responsible for enabling/disabling colors in OctoSuite -# 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 -# delete this file, the entire program breaks -system_info = [("RAM", f"{str(round(psutil.virtual_memory().total / (1024.0 ** 3)))}GB"), - ("Node", platform.node()), - ("Release", platform.release()), - ("Version", platform.version()), - ("Processor", platform.processor()), - ("Architecture", platform.architecture())] -first_banner = f""" - OCTOSUITE © 2023 Richard Mwewa - {datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')} - -""" - -print(first_banner) -system_tree = Tree(platform.system()) -for system_key, system_value in system_info: - system_tree.add(f"{system_key}: {system_value}") -xprint(system_tree) -print("\n") -while True: - try: - color_chooser = input(f"[PROMPT] Welcome, would you like to enable colors for this session? (yes/no) ").lower() - if color_chooser == "yes": - 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 == "no": - header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" - break - else: - print(f"\n[INVALID] Your response '{color_chooser}' is invalid (expected yes or no)") - - except KeyboardInterrupt: - exit(f"[WARNING] Process interrupted with Ctrl+C.") - diff --git a/octosuite/config.py b/octosuite/config.py new file mode 100644 index 0000000..5c1b9d2 --- /dev/null +++ b/octosuite/config.py @@ -0,0 +1,111 @@ +import psutil +import platform +import argparse +from rich.tree import Tree +from datetime import datetime +from rich import print as xprint + +def usage(): + return """ + Basic usage + ----------- + # Get user profile info + octosuite --module user_profile --username + + # Get organization profile info + octosuite --module org_profile --organization + + # Get repo profile info + octosuite --module repo_profile --username --repository + + + + Searching + --------- + # Search users + octosuite --module users_search --query + + # Search issues + octosuite --module issues_search --query + + # Search commits + octosuite --module commits_search --query + + # Search topics + octosuite --module topics_search --query + + # Search repositories + octosuite --module repos_search --query + """ + + +def create_parser(): + parser = argparse.ArgumentParser(description='OCTOSUITE: Advanced GitHub osint framework — by Richard Mwewa | https://about.me/rly0nheart', usage=usage()) + parser.add_argument('-m', '--module', help='module', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', + 'user_subscriptions', 'user_following', 'user_followers', 'user_follows', + 'org_profile', 'org_repos', 'org_events', 'org_member', + 'repo_profile', 'repo_contributors', 'repo_stargazers', 'repo_forks', + 'repo_issues', 'repo_releases', 'repo_path_contents', 'users_search', 'issues_search', + 'commits_search', 'topics_search', 'repos_search']) + parser.add_argument('-u', '--username', help='username') + parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)') + parser.add_argument('-org', '--organization', help='organization name') + parser.add_argument('-repo', '--repository', help='repository name') + parser.add_argument('-pn', '--path_name', help='path name (used with repo_path_contents)') + parser.add_argument('-q', '--query', help='query (used with search modules)') + parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) + parser.add_argument('-c', '--colors', help='specify to run octosuite with colors enabled', action='store_true') + return parser + + +parser = create_parser() +args = parser.parse_args() + +# This file is responsible for enabling/disabling colors in OctoSuite +# 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 +# delete this file, the entire program breaks +system_info = [("RAM", f"{str(round(psutil.virtual_memory().total / (1024.0 ** 3)))}GB"), + ("Node", platform.node()), + ("Release", platform.release()), + ("Version", platform.version()), + ("Processor", platform.processor()), + ("Architecture", platform.architecture())] +first_banner = f""" + OCTOSUITE © 2023 Richard Mwewa + {datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')} + +""" + +if args.colors: + header_title = "bold white" + red = "[red]" + white = "[white]" + green = "[green]" + red_bold = "[white bold]" + white_bold = "[white bold]" + green_bold = "[green bold]" + reset = "[/]" +else: + print(first_banner) + system_tree = Tree(platform.system()) + for system_key, system_value in system_info: + system_tree.add(f"{system_key}: {system_value}") + xprint(system_tree) + print("\n") + try: + color_chooser = input(f"[PROMPT] Welcome, would you like to enable colors for this session? (yes/no) ").lower() + if color_chooser == "yes": + header_title = "bold white" + red = "[red]" + white = "[white]" + green = "[green]" + red_bold = "[white bold]" + white_bold = "[white bold]" + green_bold = "[green bold]" + reset = "[/]" + else: + header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" + except KeyboardInterrupt: + exit(f"[WARNING] Process interrupted with Ctrl+C.") + From 331c81ac46b93fff4d60f4c197ce9f798ae0acab Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:16:39 +0200 Subject: [PATCH 06/51] Update and rename banners.py to banner.py --- octosuite/banner.py | 24 ++++++++++++++++++++++++ octosuite/banners.py | 44 -------------------------------------------- 2 files changed, 24 insertions(+), 44 deletions(-) create mode 100644 octosuite/banner.py delete mode 100644 octosuite/banners.py diff --git a/octosuite/banner.py b/octosuite/banner.py new file mode 100644 index 0000000..265fae0 --- /dev/null +++ b/octosuite/banner.py @@ -0,0 +1,24 @@ +import getpass +from rich.tree import Tree +from octosuite.config import red, white, green, reset + + +# banner.py +# This file holds the program's banner and version tag +version_tag = "3.0.0" + + +def banner(): + banner_tree = Tree(getpass.getuser()) + banner_tree.add(f"use ‘{green}help{reset}’ command for usage") + banner_tree.add(f"commands are case insensitive\n") + return f""" + _______ __ _______ __ __ + | |.----.| |_.-----.| __|.--.--.|__| |_.-----. + | - || __|| _| _ ||__ || | || | _| -__| + |_______||____||____|_____||_______||_____||__|____|_____| + v{version_tag}#dev + {white}— Advanced Github {red}OSINT{white} Framework + + +""", banner_tree diff --git a/octosuite/banners.py b/octosuite/banners.py deleted file mode 100644 index d4b53a0..0000000 --- a/octosuite/banners.py +++ /dev/null @@ -1,44 +0,0 @@ -import random -import getpass -from rich.tree import Tree -from octosuite.colors import red, white, green, reset - - -# banners.py -# This file holds the program's banners and version tag -version_tag = "3.0.0" - - -def ascii_banner(): - banner_tree = Tree(getpass.getuser()) - banner_tree.add(f"use ‘{green}help{reset}’ command for usage") - banner_tree.add(f"commands are case insensitive\n") - ascii_banners = [ - """ - _______ __ _______ __ __ - | |.----.| |_.-----.| __|.--.--.|__| |_.-----. - | - || __|| _| _ ||__ || | || | _| -__| - |_______||____||____|_____||_______||_____||__|____|_____| - """, - """ -╔═╗┌─┐┌┬┐┌─┐╔═╗┬ ┬┬┌┬┐┌─┐ -║ ║│ │ │ │╚═╗│ ││ │ ├┤ -╚═╝└─┘ ┴ └─┘╚═╝└─┘┴ ┴ └─┘ - """, - """ -░▒█▀▀▀█░█▀▄░▀█▀░▄▀▀▄░▒█▀▀▀█░█░▒█░░▀░░▀█▀░█▀▀ -░▒█░░▒█░█░░░░█░░█░░█░░▀▀▀▄▄░█░▒█░░█▀░░█░░█▀▀ -░▒█▄▄▄█░▀▀▀░░▀░░░▀▀░░▒█▄▄▄█░░▀▀▀░▀▀▀░░▀░░▀▀▀ - """, - """ - ▄▀▄ ▄▀▀ ▀█▀ ▄▀▄ ▄▀▀ █ █ █ ▀█▀ ██▀ - ▀▄▀ ▀▄▄ █ ▀▄▀ ▄██ ▀▄█ █ █ █▄▄ - """] - ascii_banner = random.choice(ascii_banners) - return banner_tree, f"""{ascii_banner} v{version_tag}#dev - {white}— Advanced Github {red}OSINT{white} Framework - - - -""" - From 5d41e07bc2669adb9846b9577af94d632b97fc8d Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:17:48 +0200 Subject: [PATCH 07/51] Update main.py --- octosuite/main.py | 60 +---------------------------------------------- 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index 1aab2bf..d39b38b 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -1,65 +1,7 @@ # import everything from the octosuite.py file -import argparse from octosuite.octosuite import * # I drifted away from the 'pythonic way' here -def usage(): - return """ - Basic usage - ----------- - # Get user profile info - octosuite --module user_profile --username - - # Get organization profile info - octosuite --module org_profile --organization - - # Get repo profile info - octosuite --module repo_profile --username --repository - - - - Searching - --------- - # Search users - octosuite --module users_search --query - - # Search issues - octosuite --module issues_search --query - - # Search commits - octosuite --module commits_search --query - - # Search topics - octosuite --module topics_search --query - - # Search repositories - octosuite --module repos_search --query - """ - - -def create_parser(): - parser = argparse.ArgumentParser(description='OCTOSUITE: Advanced GitHub osint framework — by Richard Mwewa | https://about.me/rly0nheart', usage=usage()) - parser.add_argument('-m', '--module', help='module', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', - 'user_subscriptions', 'user_following', 'user_followers', 'user_follows', - 'org_profile', 'org_repos', 'org_events', 'org_member', - 'repo_profile', 'repo_contributors', 'repo_stargazers', 'repo_forks', - 'repo_issues', 'repo_releases', 'repo_path_contents', 'users_search', 'issues_search', - 'commits_search', 'topics_search', 'repos_search']) - parser.add_argument('-u', '--username', help='username') - parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)') - parser.add_argument('-org', '--organization', help='organization name') - parser.add_argument('-repo', '--repository', help='repository name') - parser.add_argument('-pn', '--path_name', help='path name (used with repo_path_contents)') - parser.add_argument('-q', '--query', help='query (used with search modules)') - parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) - parser.add_argument('--colors', help='enable or disable colors (default: %(default)s)', type=bool, default=True) - return parser - - -parser = create_parser() -args = parser.parse_args() - - def octosuite(): try: run = Octosuite(args) @@ -67,7 +9,7 @@ def octosuite(): clear_screen() configure_logging() check_updates() - xprint(ascii_banner()[1], ascii_banner()[0]) + xprint(banner()[0], banner()[1]) if args.module == "user_profile": run.user_profile() elif args.module == "user_repos": From 53433c119bda06163127766db07f727d074f7730 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:18:17 +0200 Subject: [PATCH 08/51] Update helper.py --- octosuite/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/helper.py b/octosuite/helper.py index 9c0c10e..33c1afc 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -1,6 +1,6 @@ from rich.table import Table from rich import print as xprint -from octosuite.colors import white, green, white_bold, green_bold, header_title, reset +from octosuite.config import white, green, white_bold, green_bold, header_title, reset # helper.py # This file holds the help text for available commands. From 157cb6814276b8312962d0d5d8cea61b78982b34 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:18:37 +0200 Subject: [PATCH 09/51] Update message_prefixes.py --- octosuite/message_prefixes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/octosuite/message_prefixes.py b/octosuite/message_prefixes.py index 98e98aa..d324377 100644 --- a/octosuite/message_prefixes.py +++ b/octosuite/message_prefixes.py @@ -1,4 +1,4 @@ -from octosuite.colors import red, white, green, reset +from octosuite.config import red, white, green, reset """ message prefixes that show what @@ -10,4 +10,4 @@ WARNING = f"{white}[{red}WARNING{white}]{reset}" ERROR = f"{white}[{red}ERROR{white}]{reset}" POSITIVE = f"{white}[{green}POSITIVE{white}]{reset}" NEGATIVE = f"{white}[{red}NEGATIVE{white}]{reset}" -INFO = f"{white}[{green}INFO{white}]{reset}" \ No newline at end of file +INFO = f"{white}[{green}INFO{white}]{reset}" From bf77bc4dfe713a00772ebda5190fd8efd4285f8f Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 13:59:45 +0200 Subject: [PATCH 10/51] Update config.py --- octosuite/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/config.py b/octosuite/config.py index 5c1b9d2..e0600a9 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -54,7 +54,7 @@ def create_parser(): parser.add_argument('-pn', '--path_name', help='path name (used with repo_path_contents)') parser.add_argument('-q', '--query', help='query (used with search modules)') parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) - parser.add_argument('-c', '--colors', help='specify to run octosuite with colors enabled', action='store_true') + parser.add_argument('-c', '--colors', help='specify to run octosuite cli with colors enabled', action='store_true') return parser From f3b90f2e6d7c2393cdf23509af31acebde965271 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 14:07:12 +0200 Subject: [PATCH 11/51] Update octosuite.py --- octosuite/octosuite.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index a49820f..4a1b0a9 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -13,8 +13,9 @@ from rich.table import Table from datetime import datetime # from pyreadline3 import Readline from rich import print as xprint -from octosuite.banners import version_tag, ascii_banner -from octosuite.colors import red, white, green, header_title, reset +from octosuite.config import * +from octosuite.banner import version_tag, banner +from octosuite.config import red, white, green, header_title, reset from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ From cdf04fe4432584daaaf1bfd6c86607aa83114eb1 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:10:01 +0200 Subject: [PATCH 12/51] Update octosuite.py --- octosuite/octosuite.py | 197 +++++++++++++++++++++++------------------ 1 file changed, 111 insertions(+), 86 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 4a1b0a9..f738c74 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -7,15 +7,14 @@ import logging import getpass import requests import platform +import subprocess from rich.text import Text from rich.tree import Tree from rich.table import Table from datetime import datetime -# from pyreadline3 import Readline from rich import print as xprint -from octosuite.config import * from octosuite.banner import version_tag, banner -from octosuite.config import red, white, green, header_title, reset +from octosuite.config import red, white, green, header_title, reset, create_parser, args from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ @@ -27,18 +26,39 @@ from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_pr log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, \ log_org_profile, log_user_repos, log_user_gists, log_user_orgs, log_user_events, log_user_subscriptions, \ log_user_following, log_user_followers, log_repos_search, log_users_search, log_topics_search, log_issues_search, \ - log_commits_search # log_org_events - -# readline = Readline() - -""" -path_finder() -This method is responsible for creating/checking the availability of the (.logs, output, downloads) folders, -enabling logging to automatically log network/user activity to a file, - and logging the start of a session. -""" + log_commits_search + +def set_readline(): + if os.name == "nt": + try: + from pyreadline3 import Readline + except ImportError: + subprocess.run(['pip3', 'install', 'pyreadline3']) + readline = Readline() + else: + import readline + + def completer(text, state): + options = [i for i in commands if i.startswith(text)] + if state < len(options): + return options[state] + else: + return None + + readline.parse_and_bind("tab: complete") + readline.set_completer(completer) + + return readline + + +set_readline() + + +# path_finder() +# This function is responsible for creating/checking the availability of the (.logs, output, downloads) folders, +# enabling logging to automatically log network/user activity to a file, and logging the start of a session. def path_finder(): """ Check 3 directories (.logs, output, downloads) on startup @@ -49,15 +69,8 @@ def path_finder(): os.makedirs(directory, exist_ok=True) -""" -Configure logging and check for updates -""" - - +# Configure logging to log user activities def configure_logging(): - """ - 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", @@ -66,18 +79,19 @@ def configure_logging(): logging.info(session_opened.format(platform.node(), getpass.getuser())) +# Check if the remote tag_name from the latest release matches the one in the program +# if it does, it means the program is up to date. +# If it doesn't match, notify the user about a new release def check_updates(): response = requests.get("https://api.github.com/repos/bellingcat/octosuite/releases/latest").json() if response['tag_name'] == version_tag: - """ - Ignore if the program is up to date - """ pass else: xprint( f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") +# Delete a specified csv file def delete_csv(): xprint(f"{green}csv {white}(filename):{reset} ", end="") csv_file = input() @@ -86,76 +100,21 @@ def delete_csv(): xprint(f"{POSITIVE} {deleted_csv.format(csv_file)}") +# Clear csv files def clear_csv(): xprint(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue? (yes/no) ", end="") prompt = input().lower() if prompt == "yes": - shutil.rmtree("output", ignore_errors=True) - xprint(f"{INFO} CSV files cleared successfully!") + files = os.listdir('output') + for file in files: + if os.path.isfile(file): + os.remove(file) + xprint(f"{INFO} Cleared {len(files)} .csv files!") else: pass -def view_logs(): - logging.info(viewing_logs) - logs = os.listdir(".logs") - logs_table = Table(show_header=True, header_style=header_title) - logs_table.add_column("Log", style="dim") - logs_table.add_column("Size (bytes)") - for log in logs: - logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log))) - xprint(logs_table) - - -def read_log(): - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() - with open(os.path.join(".logs", log_file + ".log"), "r") as log: - logging.info(reading_log.format(log_file)) - xprint("\n" + log.read()) - - -def delete_log(): - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() - os.remove(os.path.join(".logs", log_file)) - logging.info(deleted_log.format(log_file)) - xprint(f"{POSITIVE} {deleted_log.format(log_file)}") - - -def clear_logs(): - xprint( - f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", - end="") - prompt = input().lower() - if prompt == "yes": - shutil.rmtree(".logs", ignore_errors=True) - xprint(f"{INFO} Logs cleared successfully!") - xprint(f"{INFO} {session_closed.format(datetime.now())}") - exit() - else: - pass - - -def exit_session(): - xprint(f"{PROMPT} This will close the current session, continue? (yes/no) ", end="") - prompt = input().lower() - if prompt == "yes": - logging.info(session_closed.format(datetime.now())) - xprint(f"{INFO} {session_closed.format(datetime.now())}") - exit() - else: - pass - - -def clear_screen(): - """ - using 'cls' on Windows machines to clear the screen, - otherwise, use 'clear' - """ - os.system('cls' if os.name == 'nt' else 'clear') - - +# View csv files def view_csv(): logging.info(viewing_csv) csv_files = os.listdir("output") @@ -167,6 +126,7 @@ def view_csv(): xprint(csv_table) +# Read csv def read_csv(): xprint(f"{green}csv {white}(filename):{reset} ", end="") csv_file = input() @@ -176,6 +136,71 @@ def read_csv(): xprint(text) +# View logs +def view_logs(): + logging.info(viewing_logs) + logs = os.listdir(".logs") + logs_table = Table(show_header=True, header_style=header_title) + logs_table.add_column("Log", style="dim") + logs_table.add_column("Size (bytes)") + for log in logs: + logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log))) + xprint(logs_table) + + +# Read log +def read_log(): + xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") + log_file = input() + with open(os.path.join(".logs", log_file + ".log"), "r") as log: + logging.info(reading_log.format(log_file)) + xprint("\n" + log.read()) + + +# Delete log +def delete_log(): + xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") + log_file = input() + os.remove(os.path.join(".logs", log_file)) + logging.info(deleted_log.format(log_file)) + xprint(f"{POSITIVE} {deleted_log.format(log_file)}") + + +# Clear logs +def clear_logs(): + xprint( + f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", end="") + prompt = input().lower() + if prompt == "yes": + files = os.listdir('.logs') + for file in files: + if os.path.isfile(file): + os.remove(file) + xprint(f"{INFO} Cleared {len(files)} .log files!") + exit() + else: + pass + + +# Exit session +def exit_session(): + xprint(f"{PROMPT} This will close the current session, continue? (yes/no) ", end="") + prompt = input().lower() + if prompt == "yes": + logging.info(session_closed.format(datetime.now())) + xprint(f"{INFO} {session_closed.format(datetime.now())}") + exit() + else: + pass + + +# Clear screen +def clear_screen(): + # Using 'cls' on Windows machines to clear the screen, + # otherwise, use 'clear' + os.system('cls' if os.name == 'nt' else 'clear') + + def about(): about_text = f""" OCTOSUITE © 2023 Richard Mwewa From ea3b8bcd716123aedf1231ac37c136532a70ed95 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:08:46 +0200 Subject: [PATCH 13/51] Update octosuite.py --- octosuite/octosuite.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index f738c74..76bb2b7 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -30,30 +30,24 @@ from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_pr -def set_readline(): - if os.name == "nt": - try: - from pyreadline3 import Readline - except ImportError: - subprocess.run(['pip3', 'install', 'pyreadline3']) - readline = Readline() - else: - import readline +if os.name == "nt": + try: + from pyreadline3 import Readline + except ImportError: + subprocess.run(['pip3', 'install', 'pyreadline3']) + readline = Readline() +else: + import readline - def completer(text, state): - options = [i for i in commands if i.startswith(text)] - if state < len(options): - return options[state] - else: - return None + def completer(text, state): + options = [i for i in commands if i.startswith(text)] + if state < len(options): + return options[state] + else: + return None - readline.parse_and_bind("tab: complete") - readline.set_completer(completer) - - return readline - - -set_readline() + readline.parse_and_bind("tab: complete") + readline.set_completer(completer) # path_finder() From c783a717a0bf49ff55ac58f4f768cf68b3135acb Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:29:17 +0200 Subject: [PATCH 14/51] Update octosuite.py --- octosuite/octosuite.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 76bb2b7..4738e53 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -85,6 +85,10 @@ def check_updates(): f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") +def list_dir_and_files(): + os.system('dir' if os.name == 'nt' else 'ls') + + # Delete a specified csv file def delete_csv(): xprint(f"{green}csv {white}(filename):{reset} ", end="") @@ -198,19 +202,18 @@ def clear_screen(): def about(): about_text = f""" OCTOSUITE © 2023 Richard Mwewa - + An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations. -With over 20+ features, Octosuite only runs on 2 external dependencies, and returns the gathered intelligence in a highly readable format. Whats new in v{version_tag}? -[{green}IMPROVED{reset}] Users will now be able to view previous commands with the ↑ ↓ arrow keys +[{green}IMPROVED{reset}] Users will now be able to view previous commands with the ↑ ↓ arrow keys (for Windows systems) [{green}IMPROVED{reset}] Removed width from tables, so that they can auto adjust +[{green}ADDED{reset}] Added the 'ls' command, which will be used to list all files and directories in the current working directory (beta) +[{green}ADDED{reset}] Added the 'cd' command, which will be used to move to a specified directory (beta) [{green}ADDED{reset}] Added the 'log:clear' command, which will be used to clear all logs [{green}ADDED{reset}] Added the 'csv:clear' command, which will be used to clear all csv files -[{green}GUI{reset}] GUI app for OSX is now available [{green}GUI{reset}] The GUI will now come as a standalone executable -[{green}GUI{reset}] Added an option to check for updates Read the wiki: https://github.com/bellingcat/octosuite/wiki GitHub REST API documentation: https://docs.github.com/rest @@ -225,7 +228,8 @@ class Octosuite: self.endpoint = 'https://api.github.com' # A list of tuples mapping commands to their methods - self.command_map = [("exit", exit_session), + self.command_map = [('ls', list_dir_and_files), + ("exit", exit_session), ("clear", clear_screen), ("about", about), ("author", self.author), From 0518f3357196b1a75e568e0055c2e5f0ee0613e1 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:52:46 +0200 Subject: [PATCH 15/51] Update main.py --- octosuite/main.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index d39b38b..addd667 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -68,17 +68,21 @@ def octosuite(): while True: xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",end="") command_input = input().lower() - print("\n") """ Iterate over the command_map and check if the user input matches any command in it [command_map], if there's a match, we return its method. If no match is found, we ignore it. """ - for command, method in run.command_map: - if command_input == command: - method() - print("\n") - else: - pass + if command_input[:2] == 'cd': + os.chdir(command_input[3:]) + elif command_input[:2] == 'ls': + os.system(f'dir {command_input[3:]}' if os.name == 'nt' else f'ls {command_input[3:]}') + else: + for command, method in run.command_map: + if command_input == command: + method() + print("\n") + else: + pass except KeyboardInterrupt: logging.warning(ctrl_c) From bd9c1f210f5e60c3839535c9f415f6766b1de3d2 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:53:21 +0200 Subject: [PATCH 16/51] Update helper.py --- octosuite/helper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/octosuite/helper.py b/octosuite/helper.py index 33c1afc..b53e70b 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -146,6 +146,8 @@ def help_command(): 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("ls", "list contents of current directory") + core_cmd_table.add_row("cd", "move to specified directory") core_cmd_table.add_row("help", "Help menu") core_cmd_table.add_row("exit", "Close session") core_cmd_table.add_row("clear", "Clear screen") From 3968f53e982d3840bbbf33b177deacb959b6ce1a Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:59:35 +0200 Subject: [PATCH 17/51] Update octosuite.py --- octosuite/octosuite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 4738e53..cd7ff31 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -209,7 +209,7 @@ An advanced and lightning fast framework for gathering open-source intelligence Whats new in v{version_tag}? [{green}IMPROVED{reset}] Users will now be able to view previous commands with the ↑ ↓ arrow keys (for Windows systems) [{green}IMPROVED{reset}] Removed width from tables, so that they can auto adjust -[{green}ADDED{reset}] Added the 'ls' command, which will be used to list all files and directories in the current working directory (beta) +[{green}ADDED{reset}] Added the 'ls' command, which will be used to list all files and directories of the specified directory (beta) [{green}ADDED{reset}] Added the 'cd' command, which will be used to move to a specified directory (beta) [{green}ADDED{reset}] Added the 'log:clear' command, which will be used to clear all logs [{green}ADDED{reset}] Added the 'csv:clear' command, which will be used to clear all csv files From 7b1fd3addce1bed9669f905de1549f79a15239d6 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:17:18 +0200 Subject: [PATCH 18/51] Update helper.py --- octosuite/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/octosuite/helper.py b/octosuite/helper.py index b53e70b..2d6b535 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -146,8 +146,8 @@ def help_command(): 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("ls", "list contents of current directory") - core_cmd_table.add_row("cd", "move to specified directory") + core_cmd_table.add_row("ls", "List contents of the specified directory") + core_cmd_table.add_row("cd", "Move to specified directory") core_cmd_table.add_row("help", "Help menu") core_cmd_table.add_row("exit", "Close session") core_cmd_table.add_row("clear", "Clear screen") From 4d0e7beea7fc4c520263635ba8ed0ba4de883e48 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:21:53 +0200 Subject: [PATCH 19/51] Update octosuite.py --- octosuite/octosuite.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index cd7ff31..96e1ba6 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -103,11 +103,8 @@ def clear_csv(): xprint(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue? (yes/no) ", end="") prompt = input().lower() if prompt == "yes": - files = os.listdir('output') - for file in files: - if os.path.isfile(file): - os.remove(file) - xprint(f"{INFO} Cleared {len(files)} .csv files!") + shutil.rmtree('output', ignore_errors=True) + xprint(f"{INFO} .csv files cleared successfully!") else: pass @@ -166,15 +163,11 @@ def delete_log(): # Clear logs def clear_logs(): - xprint( - f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", end="") + xprint(f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", end="") prompt = input().lower() if prompt == "yes": - files = os.listdir('.logs') - for file in files: - if os.path.isfile(file): - os.remove(file) - xprint(f"{INFO} Cleared {len(files)} .log files!") + shutil.rmtree('.logs', ignore_errors=True) + xprint(f"{INFO} .log files cleared successfully!") exit() else: pass From f235bc1186df09a63bb8bd67a405a0eeac2e5164 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:35:10 +0200 Subject: [PATCH 20/51] Update main.py --- octosuite/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/main.py b/octosuite/main.py index addd667..f6a5b8e 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -9,7 +9,6 @@ def octosuite(): clear_screen() configure_logging() check_updates() - xprint(banner()[0], banner()[1]) if args.module == "user_profile": run.user_profile() elif args.module == "user_repos": @@ -65,6 +64,7 @@ def octosuite(): Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) or if the 'exit' command is entered. """ + xprint(banner()[0], banner()[1]) while True: xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",end="") command_input = input().lower() From 4113c62d0977887072abc4f4e3cda6a87610328e Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:10:43 +0200 Subject: [PATCH 21/51] Update config.py --- octosuite/config.py | 61 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index e0600a9..117a714 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -8,33 +8,64 @@ from rich import print as xprint def usage(): return """ Basic usage - ----------- - # Get user profile info + =========== + + Get user profile info + --------------------- octosuite --module user_profile --username - - # Get organization profile info + + + Get user repos + -------------- + octosuite --module user_repos --username + + + Get organization profile info + ----------------------------- octosuite --module org_profile --organization + + + Get organi[sz]ation repos + ----------------------------- + octosuite --module org_repos --organization + - # Get repo profile info + Get repo profile info + --------------------- octosuite --module repo_profile --username --repository + + + Get repo forks + -------------- + octosuite --module repo_forks --username --repository Searching - --------- - # Search users + ========= + + Search users + ------------ octosuite --module users_search --query + - # Search issues + Search issues + ------------- octosuite --module issues_search --query + - # Search commits + Search commits + -------------- octosuite --module commits_search --query - # Search topics + + Search topics + ------------- octosuite --module topics_search --query - # Search repositories + + Search repositories + ------------------- octosuite --module repos_search --query """ @@ -49,12 +80,12 @@ def create_parser(): 'commits_search', 'topics_search', 'repos_search']) parser.add_argument('-u', '--username', help='username') parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)') - parser.add_argument('-org', '--organization', help='organization name') - parser.add_argument('-repo', '--repository', help='repository name') - parser.add_argument('-pn', '--path_name', help='path name (used with repo_path_contents)') + parser.add_argument('-o', '--organization', '--organisation', help='organi[sz]ation name') + parser.add_argument('-r', '--repository', help='repository name') + parser.add_argument('-p', '--path_name', help='path name (used with repo_path_contents)') parser.add_argument('-q', '--query', help='query (used with search modules)') parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) - parser.add_argument('-c', '--colors', help='specify to run octosuite cli with colors enabled', action='store_true') + parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') return parser From 9473b0278d5f6c199ff8806a9d66a7d11d9e5869 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:14:21 +0200 Subject: [PATCH 22/51] Update config.py --- octosuite/config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index 117a714..9f6beed 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -92,10 +92,10 @@ def create_parser(): parser = create_parser() args = parser.parse_args() -# This file is responsible for enabling/disabling colors in OctoSuite -# 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 -# delete this file, the entire program breaks +# This file is responsible for enabling/disabling colo[u]rs and configuring argparse in OctoSuite +# This file gets called first at start up before any other file +# config.py is the reason why users get to choose whether to enable/disable colo[u]rs, and call the program with command line arguments +# delete this file (I dare you), the entire program breaks system_info = [("RAM", f"{str(round(psutil.virtual_memory().total / (1024.0 ** 3)))}GB"), ("Node", platform.node()), ("Release", platform.release()), @@ -125,7 +125,7 @@ else: xprint(system_tree) print("\n") try: - color_chooser = input(f"[PROMPT] Welcome, would you like to enable colors for this session? (yes/no) ").lower() + color_chooser = input(f"[PROMPT] Welcome, would you like to enable colo[u]rs for this session? (yes/no) ").lower() if color_chooser == "yes": header_title = "bold white" red = "[red]" From 07ca8c486ca30f472d5a9b61897eeec0f8f55b5f Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:30:11 +0200 Subject: [PATCH 23/51] Update octosuite.py --- octosuite/octosuite.py | 72 +++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 96e1ba6..bbb50c0 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -20,7 +20,7 @@ from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIV from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv from octosuite.log_roller import ctrl_c, error, session_opened, session_closed, viewing_logs, viewing_csv, \ - deleted_log, reading_log, reading_csv, deleted_csv, file_downloading, file_downloaded, info_not_found, \ + deleted, reading_log, reading_csv, file_downloading, file_downloaded, info_not_found, \ user_not_found, org_not_found, repo_or_user_not_found, limit_output from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_profile, log_repo_path_contents, \ log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, \ @@ -91,11 +91,14 @@ def list_dir_and_files(): # Delete a specified csv file def delete_csv(): - xprint(f"{green}csv {white}(filename):{reset} ", end="") - csv_file = input() + if args.csv_file: + csv_file = args.csv_file + else: + xprint(f"{green}csv {white}(filename):{reset} ", end="") + csv_file = input() os.remove(os.path.join("output", csv_file)) - logging.info(deleted_csv.format(csv_file)) - xprint(f"{POSITIVE} {deleted_csv.format(csv_file)}") + logging.info(deleted.format(csv_file)) + xprint(f"{POSITIVE} {deleted.format(csv_file)}") # Clear csv files @@ -123,9 +126,12 @@ def view_csv(): # Read csv def read_csv(): - xprint(f"{green}csv {white}(filename):{reset} ", end="") - csv_file = input() - with open(os.path.join("output", csv_file + ".csv"), "r") as file: + if args.csv_file: + csv_file = args.csv_file + else: + xprint(f"{green}csv {white}(filename.csv):{reset} ", end="") + csv_file = input() + with open(os.path.join("output", csv_file), "r") as file: logging.info(reading_csv.format(csv_file)) text = Text(file.read()) xprint(text) @@ -145,6 +151,8 @@ def view_logs(): # Read log def read_log(): + if args.log_file: + log_file = args.log_file xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") log_file = input() with open(os.path.join(".logs", log_file + ".log"), "r") as log: @@ -154,11 +162,14 @@ def read_log(): # Delete log def delete_log(): - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() + if args.log_file: + log_file = args.log_file + else: + xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") + log_file = input() os.remove(os.path.join(".logs", log_file)) - logging.info(deleted_log.format(log_file)) - xprint(f"{POSITIVE} {deleted_log.format(log_file)}") + logging.info(deleted.format(log_file)) + xprint(f"{POSITIVE} {deleted.format(log_file)}") # Clear logs @@ -277,6 +288,43 @@ class Octosuite: ("csv:delete", delete_csv), ("csv:clear", clear_csv)] + # Arguments map will be used to run Octosuite with argparse + self.argument_map = [("user_profile", self.user_profile), + ("user_repos", self.user_repos), + ("user_gists", self.user_gists), + ("user_orgs", self.user_orgs), + ("user_events", self.user_events), + ("user_subscriptions", self.user_subscriptions), + ("user_following", self.user_following), + ("user_followers", self.user_followers), + ("user_follows", self.user_follows), + ("users_search", self.users_search), + ("issues_search", self.issues_search), + ("commits_search", self.commits_search), + ("topics_search", self.topics_search), + ("repos_search", self.repos_search), + ("org_profile", self.org_profile), + ("org_repos", self.org_repos), + ("org_events", self.org_events), + ("org_member", self.org_member), + ("repo_profile", self.repo_profile), + ("repo_contributors", self.repo_contributors), + ("repo_stargazers", self.repo_stargazers), + ("repo_forks", self.repo_forks), + ("repo_issues", self.repo_issues), + ("repo_releases", self.repo_releases), + ("repo_path_contents", self.path_contents), + ("view_logs", view_logs), + ("read_log", read_log), + ("delete_log", delete_log), + ("clear_logs", clear_logs), + ("view_csv", view_csv), + ("read_csv", read_csv), + ("delete_csv", delete_csv), + ("clear_csv", clear_csv), + ("about", about), + ("author", self.author)] + # Path attribute self.path_attrs = ['size', 'type', 'path', 'sha', 'html_url'] # Path attribute dictionary From 4db8300e2cc2f7167aabffaff0395247d8507959 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:31:43 +0200 Subject: [PATCH 24/51] Update log_roller.py --- octosuite/log_roller.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/octosuite/log_roller.py b/octosuite/log_roller.py index 749be36..c7c4c62 100644 --- a/octosuite/log_roller.py +++ b/octosuite/log_roller.py @@ -8,10 +8,8 @@ session_opened = "Opened new session on {}:{}" session_closed = "Session closed at {}." viewing_logs = "Viewing logs" viewing_csv = "Viewing CSV file(s)..." -deleted_log = "Deleted log: {}" -reading_log = "Reading log: {}" -reading_csv = 'Reading csv: {}' -deleted_csv = 'Deleted csv: {}' +deleted = "Deleted: {}" +reading = "Reading: {}" file_downloading = "Downloading: {}" file_downloaded = "Downloaded: downloads/{}" info_not_found = "Information not found: {}, {}, {}" From b6908a19f4e32d4102591ed28b4bea98e9072db1 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:33:26 +0200 Subject: [PATCH 25/51] Update octosuite.py --- octosuite/octosuite.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index bbb50c0..18bdfef 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -20,7 +20,7 @@ from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIV from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv from octosuite.log_roller import ctrl_c, error, session_opened, session_closed, viewing_logs, viewing_csv, \ - deleted, reading_log, reading_csv, file_downloading, file_downloaded, info_not_found, \ + deleted, reading, file_downloading, file_downloaded, info_not_found, \ user_not_found, org_not_found, repo_or_user_not_found, limit_output from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_profile, log_repo_path_contents, \ log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, \ @@ -132,7 +132,7 @@ def read_csv(): xprint(f"{green}csv {white}(filename.csv):{reset} ", end="") csv_file = input() with open(os.path.join("output", csv_file), "r") as file: - logging.info(reading_csv.format(csv_file)) + logging.info(reading.format(csv_file)) text = Text(file.read()) xprint(text) @@ -156,7 +156,7 @@ def read_log(): xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") log_file = input() with open(os.path.join(".logs", log_file + ".log"), "r") as log: - logging.info(reading_log.format(log_file)) + logging.info(reading.format(log_file)) xprint("\n" + log.read()) From 3da2b91fb2e6538883d747334800236b74bb5ff8 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:33:59 +0200 Subject: [PATCH 26/51] Update config.py --- octosuite/config.py | 104 ++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index 9f6beed..f5cd40d 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -7,85 +7,135 @@ from rich import print as xprint def usage(): return """ - Basic usage + Basic Usage =========== - Get user profile info + Get User Profile Info --------------------- - octosuite --module user_profile --username + octosuite --method user_profile --username - Get user repos + Get User Repos -------------- - octosuite --module user_repos --username + octosuite --method user_repos --username - Get organization profile info + Get Organi[sz]ation Profile Info ----------------------------- - octosuite --module org_profile --organization + octosuite --method org_profile --organization - Get organi[sz]ation repos + Get Organi[sz]ation Repos ----------------------------- - octosuite --module org_repos --organization + octosuite --method org_repos --organization - Get repo profile info + Get Repo Profile Info --------------------- - octosuite --module repo_profile --username --repository + octosuite --method repo_profile --username --repository - Get repo forks + Get Repo Forks -------------- - octosuite --module repo_forks --username --repository + octosuite --method repo_forks --username --repository Searching ========= - Search users + Search Users ------------ - octosuite --module users_search --query + octosuite --method users_search --query - Search issues + Search Issues ------------- - octosuite --module issues_search --query + octosuite --method issues_search --query - Search commits + Search Commits -------------- - octosuite --module commits_search --query + octosuite --method commits_search --query - Search topics + Search Topics ------------- - octosuite --module topics_search --query + octosuite --method topics_search --query - Search repositories + Search Repositories ------------------- - octosuite --module repos_search --query + octosuite --method repos_search --query + + + Log Management + ============== + + View logs + --------- + octosuite --method view_logs + + + Read log + -------- + octosuite --method read_log --log_file + + + Delete log + ---------- + octosuite --method delete_log --log_file + + + Clear logs + ---------- + octosuite --method clear_logs + + + + CSV Management + ============== + + View CSV + --------- + octosuite --method view_csv + + + Read CSV + -------- + octosuite --method read_csv --csv_file + + + Delete CSV + ---------- + octosuite --method delete_csv --csv_file + + + Clear CSV's + ----------- + octosuite --method clear_csv """ def create_parser(): parser = argparse.ArgumentParser(description='OCTOSUITE: Advanced GitHub osint framework — by Richard Mwewa | https://about.me/rly0nheart', usage=usage()) - parser.add_argument('-m', '--module', help='module', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', + parser.add_argument('-m', '--method', help='method', choices=['user_profile', 'user_repos', 'user_gists', 'user_orgs', 'user_events', 'user_subscriptions', 'user_following', 'user_followers', 'user_follows', 'org_profile', 'org_repos', 'org_events', 'org_member', 'repo_profile', 'repo_contributors', 'repo_stargazers', 'repo_forks', 'repo_issues', 'repo_releases', 'repo_path_contents', 'users_search', 'issues_search', - 'commits_search', 'topics_search', 'repos_search']) + 'commits_search', 'topics_search', 'repos_search', 'view_logs', 'read_log', 'delete_log', + 'clear_logs', 'view_csv', 'read_csv', 'delete_csv', 'clear_csv', 'about', 'author']) parser.add_argument('-u', '--username', help='username') parser.add_argument('-uB', '--username_b', help='username_B (used with user_follows)') parser.add_argument('-o', '--organization', '--organisation', help='organi[sz]ation name') parser.add_argument('-r', '--repository', help='repository name') parser.add_argument('-p', '--path_name', help='path name (used with repo_path_contents)') - parser.add_argument('-q', '--query', help='query (used with search modules)') - parser.add_argument('-l', '--limit', help='output limit (used with modules that return results in bulk) (default: %(default)s)', default=10) + parser.add_argument('-q', '--query', help='query (used with search methods)') + parser.add_argument('-l', '--limit', help='output limit (used with methods that return results in bulk) (default: %(default)s)', default=10) parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') + parser.add_argument('--csv_file', help='specify a csv file (used with csv management methods)') + parser.add_argument('--log_file', help='specify a log file (used with logs management methods)') return parser From 8fc14d3ec455d80b9fbde8d57deeed4d898c3219 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:34:44 +0200 Subject: [PATCH 27/51] Update main.py --- octosuite/main.py | 54 ++++------------------------------------------- 1 file changed, 4 insertions(+), 50 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index f6a5b8e..8eb5f94 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -9,56 +9,10 @@ def octosuite(): clear_screen() configure_logging() check_updates() - if args.module == "user_profile": - run.user_profile() - elif args.module == "user_repos": - run.user_repos() - elif args.module == "user_gists": - run.user_gists() - elif args.module == "user_orgs": - run.user_orgs() - elif args.module == "user_events": - run.user_events() - elif args.module == "user_subscriptions": - run.user_subscriptions() - elif args.module == "user_following": - run.user_following() - elif args.module == "user_followers": - run.user_followers() - elif args.module == "user_follows": - run.user_follows() - elif args.module == "users_search": - run.users_search() - elif args.module == "issues_search": - run.issues_search() - elif args.module == "commits_search": - run.commits_search() - elif args.module == "topics_search": - run.topics_search() - elif args.module == "repos_search": - run.repos_search() - elif args.module == "org_profile": - run.org_profile() - elif args.module == "org_repos": - run.org_repos() - elif args.module == "org_events": - run.org_events() - elif args.module == "org_member": - run.org_member() - elif args.module == "repo_profile": - run.repo_profile() - elif args.module == "repo_contributors": - run.repo_contributors() - elif args.module == "repo_stargazers": - run.repo_stargazers() - elif args.module == "repo_forks": - run.repo_forks() - elif args.module == "repo_issues": - run.repo_issues() - elif args.module == "repo_releases": - run.repo_releases() - elif args.module == "repo_path_contents": - run.path_contents() + if args: + for argument, method in run.argument_map: + if args.method == argument: + method() else: """ Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) From 49215fd03edce0861c43992e11236a6b85337ecf Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:36:29 +0200 Subject: [PATCH 28/51] Update main.py --- octosuite/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/octosuite/main.py b/octosuite/main.py index 8eb5f94..e4703b9 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -10,6 +10,10 @@ def octosuite(): configure_logging() check_updates() if args: + """ + Iterate over the argument_map and check if the passed command line argument matches any argument in it [argument_map], + if there's a match, we return its method. If no match is found, we do nothing (which will return the usage). + """ for argument, method in run.argument_map: if args.method == argument: method() From 0677b901f77a496fcffbd223a56a904971c9af8e Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:41:32 +0200 Subject: [PATCH 29/51] Update config.py --- octosuite/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/octosuite/config.py b/octosuite/config.py index f5cd40d..641845e 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -69,6 +69,7 @@ def usage(): octosuite --method repos_search --query + Log Management ============== From 52c4101a7a23438d059b4c573b128260b7f8aec2 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 24 Dec 2022 12:45:55 +0200 Subject: [PATCH 30/51] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1932b79..682e99f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![logo](https://user-images.githubusercontent.com/74001397/175805580-fffc96d4-e0ef-48bb-a55c-80b2da3e714d.png) -A framework fro gathering osint on GitHub users, repositories and organizations +A framework for gathering osint on GitHub users, repositories and organizations [![Upload Python Package](https://github.com/bellingcat/octosuite/actions/workflows/python-publish.yml/badge.svg)](https://github.com/bellingcat/octosuite/actions/workflows/python-publish.yml) [![CodeQL](https://github.com/bellingcat/octosuite/actions/workflows/codeql.yml/badge.svg)](https://github.com/bellingcat/octosuite/actions/workflows/codeql.yml) From 34c7cd391819250677111cac7e57d5f4f87717df Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 24 Dec 2022 12:46:47 +0200 Subject: [PATCH 31/51] Update message_prefixes.py --- octosuite/message_prefixes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/message_prefixes.py b/octosuite/message_prefixes.py index d324377..69c4988 100644 --- a/octosuite/message_prefixes.py +++ b/octosuite/message_prefixes.py @@ -6,7 +6,7 @@ a notification in OctoSuite might be all about. This might not be very important but I think it's better to know the severity of the notifications you get in a program. """ PROMPT = f"{white}[{green}PROMPT{white}]{reset}" -WARNING = f"{white}[{red}WARNING{white}]{reset}" +WARNING = f"{white}[{yellow}WARNING{white}]{reset}" ERROR = f"{white}[{red}ERROR{white}]{reset}" POSITIVE = f"{white}[{green}POSITIVE{white}]{reset}" NEGATIVE = f"{white}[{red}NEGATIVE{white}]{reset}" From 4c7109f4f0760a620b33e1515169d6a717775800 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:47:48 +0200 Subject: [PATCH 32/51] Update log_roller.py --- octosuite/log_roller.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/octosuite/log_roller.py b/octosuite/log_roller.py index c7c4c62..d47b8af 100644 --- a/octosuite/log_roller.py +++ b/octosuite/log_roller.py @@ -16,7 +16,6 @@ info_not_found = "Information not found: {}, {}, {}" user_not_found = "User not found: @{}" org_not_found = "Organization not found: @{}" repo_or_user_not_found = "Repository or User not found: {}, @{}" -prompt_log_csv = "Do you wish to log this output to a CSV file? (yes/no) " +prompt_log_csv = "Do you wish to log this output to a CSV file?" logged_to_csv = "Output logged: {}" -logging_skipped = "Logging skipped: {}" -limit_output = "Limit '{}' output to how many? (1-100) " +limit_output = "Limit '{}' output to how many? (1-100)" From 64c1063ea5aeb3d3082702676a9f3e215fa2adb4 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:48:15 +0200 Subject: [PATCH 33/51] Update banner.py --- octosuite/banner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/octosuite/banner.py b/octosuite/banner.py index 265fae0..1573a92 100644 --- a/octosuite/banner.py +++ b/octosuite/banner.py @@ -1,6 +1,5 @@ import getpass -from rich.tree import Tree -from octosuite.config import red, white, green, reset +from octosuite.config import red, white, green, reset, Tree # banner.py From 272fcc36eda0e645241a93673b3adf44176a373f Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:48:39 +0200 Subject: [PATCH 34/51] Update config.py --- octosuite/config.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index 641845e..8f5fab8 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -1,10 +1,15 @@ import psutil import platform import argparse + from rich.tree import Tree +from rich.text import Text +from rich.table import Table from datetime import datetime +from rich.prompt import Prompt from rich import print as xprint + def usage(): return """ Basic Usage @@ -137,6 +142,7 @@ def create_parser(): parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') parser.add_argument('--csv_file', help='specify a csv file (used with csv management methods)') parser.add_argument('--log_file', help='specify a log file (used with logs management methods)') + parser.add_argument('--log_csv', help='specify to log output to a csv', action='store_true') return parser @@ -164,6 +170,7 @@ if args.colors: red = "[red]" white = "[white]" green = "[green]" + yellow = "[yellow]" red_bold = "[white bold]" white_bold = "[white bold]" green_bold = "[green bold]" @@ -176,18 +183,19 @@ else: xprint(system_tree) print("\n") try: - color_chooser = input(f"[PROMPT] Welcome, would you like to enable colo[u]rs for this session? (yes/no) ").lower() + color_chooser = Prompt.ask(f"Welcome, would you like to enable colo(u)rs for this session?", choices=['yes', 'no']) if color_chooser == "yes": header_title = "bold white" red = "[red]" white = "[white]" green = "[green]" + yellow = "[yellow]" red_bold = "[white bold]" white_bold = "[white bold]" green_bold = "[green bold]" reset = "[/]" else: - header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" + header_title = red = white = green = red_bold = white_bold = green_bold = reset = yellow = "" except KeyboardInterrupt: exit(f"[WARNING] Process interrupted with Ctrl+C.") From 5bff3ccc12cc4618588f760102a23dc2daf7bc6c Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:49:07 +0200 Subject: [PATCH 35/51] Update csv_loggers.py --- octosuite/csv_loggers.py | 419 +++++++++++++-------------------------- 1 file changed, 139 insertions(+), 280 deletions(-) diff --git a/octosuite/csv_loggers.py b/octosuite/csv_loggers.py index b46901f..ef39394 100644 --- a/octosuite/csv_loggers.py +++ b/octosuite/csv_loggers.py @@ -21,22 +21,16 @@ def log_org_profile(response): response.json()['is_verified'], response.json()['has_organization_projects'], response.json()['has_repository_projects'], response.json()['created_at'], response.json()['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{response.json()['name']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(org_profile_fields) - write_csv.writerow(org_profile_row) + + with open(os.path.join("output", f"{response.json()['name']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(org_profile_fields) + write_csv.writerow(org_profile_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Creating a .csv file of a user' profile def log_user_profile(response): user_profile_fields = ['Profile photo', 'Name', 'Username', 'ID', 'Node ID', 'Bio', 'Blog', 'Location', 'Followers', @@ -49,21 +43,15 @@ def log_user_profile(response): response.json()['public_gists'], response.json()['public_repos'], response.json()['company'], response.json()['hireable'], response.json()['site_admin'], response.json()['created_at'], response.json()['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{response.json()['login']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_profile_fields) - write_csv.writerow(user_profile_row) + + with open(os.path.join("output", f"{response.json()['login']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_profile_fields) + write_csv.writerow(user_profile_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - # create .csv for repository profile def log_repo_profile(response): @@ -81,66 +69,47 @@ def log_repo_profile(response): response.json()['has_pages'], response.json()['has_projects'], response.json()['has_issues'], response.json()['has_downloads'], response.json()['pushed_at'], response.json()['created_at'], response.json()['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{response.json()['name']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_profile_fields) - write_csv.writerow(repo_profile_row) - + + with open(os.path.join("output", f"{response.json()['name']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_profile_fields) + write_csv.writerow(repo_profile_row) + logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - + # create .csv for repository path contents def log_repo_path_contents(content, repo_name): path_content_fields = ['Filename', 'Size (bytes)', 'Type', 'Path', 'SHA', 'URL'] path_content_row = [content['name'], content['size'], content['type'], content['path'], content['sha'], content['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{content['name']}_content_from_{repo_name}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(path_content_fields) - write_csv.writerow(path_content_row) + + with open(os.path.join("output", f"{content['name']}_content_from_{repo_name}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(path_content_fields) + write_csv.writerow(path_content_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # create .csv for repository stargazer def log_repo_stargazers(stargazer, repo_name): user_follower_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL'] user_follower_row = [stargazer['avatar_url'], stargazer['login'], stargazer['id'], stargazer['node_id'], stargazer['gravatar_id'], stargazer['type'], stargazer['site_admin'], stargazer['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{stargazer['login']}_stargazer_of_{repo_name}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_follower_fields) - write_csv.writerow(user_follower_row) + + with open(os.path.join("output", f"{stargazer['login']}_stargazer_of_{repo_name}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_follower_fields) + write_csv.writerow(user_follower_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - -# create .csv for repository forks + # create .csv for repository forks def log_repo_forks(fork, count): repo_fork_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', @@ -152,22 +121,16 @@ def log_repo_forks(fork, count): fork['fork'], fork['allow_forking'], fork['private'], fork['archived'], fork['is_template'], fork['has_wiki'], fork['has_pages'], fork['has_projects'], fork['has_issues'], fork['has_downloads'], fork['pushed_at'], fork['created_at'], fork['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{fork['name']}_fork_{count}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_fork_fields) - write_csv.writerow(repo_fork_row) + + with open(os.path.join("output", f"{fork['name']}_fork_{count}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_fork_fields) + write_csv.writerow(repo_fork_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # create .csv for repository issues def log_repo_issues(issue, repo_name): repo_issue_fields = ['Title', 'ID', 'Node ID', 'Number', 'State', 'Reactions', 'Comments', 'Milestone', 'Assignee', @@ -177,22 +140,16 @@ def log_repo_issues(issue, repo_name): issue['reactions'], issue['comments'], issue['milestone'], issue['assignee'], issue['assignees'], issue['author_association'], issue['labels'], issue['locked'], issue['active_lock_reason'], issue['closed_at'], issue['created_at'], issue['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{repo_name}_issue_{issue['id']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_issue_fields) - write_csv.writerow(repo_issue_row) + + with open(os.path.join("output", f"{repo_name}_issue_{issue['id']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_issue_fields) + write_csv.writerow(repo_issue_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # create .csv for repository releases def log_repo_releases(release, repo_name): repo_release_fields = ['Name', 'ID', 'Node ID', 'Tag', 'Branch', 'Assets', 'Is draft?', 'Is prerelease?', @@ -200,22 +157,16 @@ def log_repo_releases(release, repo_name): repo_release_row = [release['name'], release['id'], release['node_id'], release['tag_name'], release['target_commitish'], release['assets'], release['draft'], release['prerelease'], release['created_at'], release['published_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{repo_name}_release_{release['name']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_release_fields) - write_csv.writerow(repo_release_row) + + with open(os.path.join("output", f"{repo_name}_release_{release['name']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_release_fields) + write_csv.writerow(repo_release_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv file for repository contributors def log_repo_contributors(contributor, repo_name): repo_contributor_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', @@ -223,42 +174,30 @@ def log_repo_contributors(contributor, repo_name): repo_contributor_row = [contributor['avatar_url'], contributor['login'], contributor['id'], contributor['node_id'], contributor['gravatar_id'], contributor['type'], contributor['site_admin'], contributor['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{contributor['login']}_contributor_of_{repo_name}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_contributor_fields) - write_csv.writerow(repo_contributor_row) + + with open(os.path.join("output", f"{contributor['login']}_contributor_of_{repo_name}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_contributor_fields) + write_csv.writerow(repo_contributor_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - print(f"{INFO} {logging_skipped}\n") - - + # Create .csv for organization' events def log_repo_events(event, organization): org_event_fields = ['ID', 'Type', 'Created at', 'Payload'] org_event_row = [event['id'], event['type'], event['created_at'], event['payload']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{organization}_event_{event['id']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(org_event_fields) - write_csv.writerow(org_event_row) + + with open(os.path.join("output", f"{organization}_event_{event['id']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(org_event_fields) + write_csv.writerow(org_event_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv for organization' repositories def log_org_repos(repository, organization): org_repo_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', @@ -273,22 +212,16 @@ def log_org_repos(repository, organization): repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{repository['name']}_repository_of_{organization}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(org_repo_fields) - write_csv.writerow(org_repo_row) + + with open(os.path.join("output", f"{repository['name']}_repository_of_{organization}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(org_repo_fields) + write_csv.writerow(org_repo_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user' repositories def log_user_repos(repository, username): user_repo_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', @@ -303,109 +236,79 @@ def log_user_repos(repository, username): repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{repository['name']}_{username}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_repo_fields) - write_csv.writerow(user_repo_row) + + with open(os.path.join("output", f"{repository['name']}_{username}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_repo_fields) + write_csv.writerow(user_repo_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user events def log_user_events(event): user_event_fields = ['Actor', 'Type', 'Repository', 'Created at', 'Payload'] user_event_row = [event['actor']['login'], event['type'], event['repo']['name'], event['created_at'], event['payload']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{event['actor']['login']}_event_{event['id']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_event_fields) - write_csv.writerow(user_event_row) + + with open(os.path.join("output", f"{event['actor']['login']}_event_{event['id']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_event_fields) + write_csv.writerow(user_event_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user gists def log_user_gists(gist): user_gist_fields = ['ID', 'Node ID', 'About', 'Comments', 'Files', 'Git Push URL', 'Is public?', 'Is truncated?', 'Updated at'] user_gist_row = [gist['id'], gist['node_id'], gist['description'], gist['comments'], gist['files'], gist['git_push_url'], gist['public'], gist['truncated'], gist['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{gist['id']}_gists_{gist['owner']['login']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_gist_fields) - write_csv.writerow(user_gist_row) + + with open(os.path.join("output", f"{gist['id']}_gists_{gist['owner']['login']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_gist_fields) + write_csv.writerow(user_gist_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user followers def log_user_followers(follower, username): user_follower_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL'] user_follower_row = [follower['avatar_url'], follower['login'], follower['id'], follower['node_id'], follower['gravatar_id'], follower['type'], follower['site_admin'], follower['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(f"output/{follower['login']}_follower_of_{username}.csv", 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_follower_fields) - write_csv.writerow(user_follower_row) + + with open(f"output/{follower['login']}_follower_of_{username}.csv", 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_follower_fields) + write_csv.writerow(user_follower_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user following def log_user_following(user, username): user_following_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL'] user_following_row = [user['avatar_url'], user['login'], user['id'], user['node_id'], user['gravatar_id'], user['type'], user['site_admin'], user['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{user['login']}_followed_by_{username}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_following_fields) - write_csv.writerow(user_following_row) + + with open(os.path.join("output", f"{user['login']}_followed_by_{username}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_following_fields) + write_csv.writerow(user_following_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user' subscriptions def log_user_subscriptions(repository, username): user_subscription_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', @@ -422,65 +325,47 @@ def log_user_subscriptions(repository, username): repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{username}_subscriptions_{repository['name']}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_subscription_fields) - write_csv.writerow(user_subscription_row) + + with open(os.path.join("output", f"{username}_subscriptions_{repository['name']}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_subscription_fields) + write_csv.writerow(user_subscription_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # .csv for user organizations def log_user_orgs(organization, username): user_org_fields = ['Profile photo', 'Name', 'ID', 'Node ID', 'URL', 'About'] user_org_row = [organization['avatar_url'], organization['login'], organization['id'], organization['node_id'], organization['url'], organization['description']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{organization['login']}_{username}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_org_fields) - write_csv.writerow(user_org_row) + + with open(os.path.join("output", f"{organization['login']}_{username}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_org_fields) + write_csv.writerow(user_org_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv for user search def log_users_search(user, query): user_search_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL'] user_search_row = [user['avatar_url'], user['login'], user['id'], user['node_id'], user['gravatar_id'], user['type'], user['site_admin'], user['html_url']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{user['login']}_user_search_result_for_{query}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(user_search_fields) - write_csv.writerow(user_search_row) + + with open(os.path.join("output", f"{user['login']}_user_search_result_for_{query}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(user_search_fields) + write_csv.writerow(user_search_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv for repository search def log_repos_search(repository, query): repo_search_fields = ['Name', 'ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', @@ -496,46 +381,32 @@ def log_repos_search(repository, query): repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{repository['name']}_repository_search_result_for_{query}.csv"), - 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(repo_search_fields) - write_csv.writerow(repo_search_row) + + with open(os.path.join("output", f"{repository['name']}_repository_search_result_for_{query}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(repo_search_fields) + write_csv.writerow(repo_search_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - # Create .csv for topic search - - + +# Create .csv for topic search def log_topics_search(topic, query): topic_search_fields = ['Name', 'Score', 'Curated', 'Featured', 'Display name', 'Created by', 'Created at', 'Updated at'] topic_search_row = [topic['name'], topic['score'], topic['curated'], topic['featured'], topic['display_name'], topic['created_by'], topic['created_at'], topic['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{topic['name']}_topic_search_result_for_{query}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(topic_search_fields) - write_csv.writerow(topic_search_row) + + with open(os.path.join("output", f"{topic['name']}_topic_search_result_for_{query}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(topic_search_fields) + write_csv.writerow(topic_search_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv for issues search def log_issues_search(issue, query): issue_search_fields = ['Title', 'ID', 'Node ID', 'Number', 'State', 'Reactions', 'Comments', 'Milestone', @@ -545,40 +416,28 @@ def log_issues_search(issue, query): issue['reactions'], issue['comments'], issue['milestone'], issue['assignee'], issue['assignees'], issue['author_association'], issue['labels'], issue['locked'], issue['active_lock_reason'], issue['closed_at'], issue['created_at'], issue['updated_at']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{issue['id']}_issue_search_result_for_{query}.csv"), 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(issue_search_fields) - write_csv.writerow(issue_search_row) + + with open(os.path.join("output", f"{issue['id']}_issue_search_result_for_{query}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(issue_search_fields) + write_csv.writerow(issue_search_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") - - + # Create .csv for commits search def log_commits_search(commit, query): commit_search_fields = ['SHA', 'Author', 'Username', 'Email', 'Committer', 'Repository', 'URL', 'Description'] commit_search_row = [commit['commit']['tree']['sha'], commit['commit']['author']['name'], commit['author']['login'], commit['commit']['author']['email'], commit['commit']['committer']['name'], commit['repository']['full_name'], commit['html_url'], commit['commit']['message']] - xprint(f"\n{PROMPT} {prompt_log_csv}", end=""); - prompt = input().lower() - if prompt == "yes": - with open(os.path.join("output", f"{commit['commit']['tree']['sha']}_commit_search_result_for_{query}.csv"), - 'w') as file: - write_csv = csv.writer(file) - write_csv.writerow(commit_search_fields) - write_csv.writerow(commit_search_row) + + with open(os.path.join("output", f"{commit['commit']['tree']['sha']}_commit_search_result_for_{query}.csv"), 'w') as file: + write_csv = csv.writer(file) + write_csv.writerow(commit_search_fields) + write_csv.writerow(commit_search_row) logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - - else: - logging.info(logging_skipped.format(prompt)) - xprint(f"{INFO} {logging_skipped.format(prompt)}") + From f9d56c51a07b08d8f65f663b8709ae5660860ce8 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:49:52 +0200 Subject: [PATCH 36/51] Update helper.py --- octosuite/helper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/octosuite/helper.py b/octosuite/helper.py index 2d6b535..fbb8277 100644 --- a/octosuite/helper.py +++ b/octosuite/helper.py @@ -1,6 +1,5 @@ from rich.table import Table -from rich import print as xprint -from octosuite.config import white, green, white_bold, green_bold, header_title, reset +from octosuite.config import Tree, xprint, white, green, white_bold, green_bold, header_title, reset # helper.py # This file holds the help text for available commands. From 7531aa4f028e16bd38b323271003ca00c48cdae6 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:50:36 +0200 Subject: [PATCH 37/51] Update main.py --- octosuite/main.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index e4703b9..b09bd73 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -4,7 +4,7 @@ from octosuite.octosuite import * # I drifted away from the 'pythonic way' here def octosuite(): try: - run = Octosuite(args) + run = Octosuite() path_finder() clear_screen() configure_logging() @@ -17,30 +17,30 @@ def octosuite(): for argument, method in run.argument_map: if args.method == argument: method() - else: + print("\n") + + """ + Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) + or if the 'exit' command is entered. + """ + xprint(banner()[0], banner()[1]) + while True: + command_input = Prompt.ask(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}") """ - Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) - or if the 'exit' command is entered. + Iterate over the command_map and check if the user input matches any command in it [command_map], + if there's a match, we return its method. If no match is found, we ignore it. """ - xprint(banner()[0], banner()[1]) - while True: - xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",end="") - command_input = input().lower() - """ - Iterate over the command_map and check if the user input matches any command in it [command_map], - if there's a match, we return its method. If no match is found, we ignore it. - """ - if command_input[:2] == 'cd': - os.chdir(command_input[3:]) - elif command_input[:2] == 'ls': - os.system(f'dir {command_input[3:]}' if os.name == 'nt' else f'ls {command_input[3:]}') - else: - for command, method in run.command_map: - if command_input == command: - method() - print("\n") - else: - pass + if command_input[:2] == 'cd': + os.chdir(command_input[3:]) + elif command_input[:2] == 'ls': + os.system(f'dir {command_input[3:]}' if os.name == 'nt' else f'ls {command_input[3:]}') + else: + for command, method in run.command_map: + if command_input == command: + method() + print("\n") + else: + pass except KeyboardInterrupt: logging.warning(ctrl_c) From 3bd6af4f28bed363bccd46df6a2f2e8d13892468 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:50:59 +0200 Subject: [PATCH 38/51] Update message_prefixes.py --- octosuite/message_prefixes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/message_prefixes.py b/octosuite/message_prefixes.py index 69c4988..9b71391 100644 --- a/octosuite/message_prefixes.py +++ b/octosuite/message_prefixes.py @@ -1,4 +1,4 @@ -from octosuite.config import red, white, green, reset +from octosuite.config import red, white, green, yellow, reset """ message prefixes that show what From ee4af7d266e8ba6c7bf8ded98ad41b1c429dea61 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:51:56 +0200 Subject: [PATCH 39/51] Update octosuite.py --- octosuite/octosuite.py | 390 ++++++++++++++++++++++------------------- 1 file changed, 207 insertions(+), 183 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 18bdfef..e6fd82c 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -8,20 +8,16 @@ import getpass import requests import platform import subprocess -from rich.text import Text -from rich.tree import Tree -from rich.table import Table from datetime import datetime -from rich import print as xprint from octosuite.banner import version_tag, banner -from octosuite.config import red, white, green, header_title, reset, create_parser, args +from octosuite.config import Tree, Text, Table, Prompt, xprint, create_parser, args, red, white, green, yellow, header_title, reset from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ logs_command, csv_command, org_command, source, org, repo, user, search, logs, csv from octosuite.log_roller import ctrl_c, error, session_opened, session_closed, viewing_logs, viewing_csv, \ deleted, reading, file_downloading, file_downloaded, info_not_found, \ - user_not_found, org_not_found, repo_or_user_not_found, limit_output + user_not_found, org_not_found, repo_or_user_not_found, limit_output, prompt_log_csv from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_profile, log_repo_path_contents, \ log_repo_contributors, log_repo_stargazers, log_repo_forks, log_repo_issues, log_repo_releases, log_org_repos, \ log_org_profile, log_user_repos, log_user_gists, log_user_orgs, log_user_events, log_user_subscriptions, \ @@ -29,7 +25,6 @@ from octosuite.csv_loggers import log_org_profile, log_user_profile, log_repo_pr log_commits_search - if os.name == "nt": try: from pyreadline3 import Readline @@ -74,15 +69,14 @@ def configure_logging(): # Check if the remote tag_name from the latest release matches the one in the program -# if it does, it means the program is up to date. +# if it does, it means the program is up-to-date. # If it doesn't match, notify the user about a new release def check_updates(): response = requests.get("https://api.github.com/repos/bellingcat/octosuite/releases/latest").json() if response['tag_name'] == version_tag: pass else: - xprint( - f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") + xprint(f"[{green}UPDATE{reset}] A new release of Octosuite is available ({response['tag_name']}). Run 'pip install --upgrade octosuite' to get the updates.\n") def list_dir_and_files(): @@ -94,8 +88,7 @@ def delete_csv(): if args.csv_file: csv_file = args.csv_file else: - xprint(f"{green}csv {white}(filename):{reset} ", end="") - csv_file = input() + csv_file = Prompt.ask(f"{green}csv {white}(filename){reset}") os.remove(os.path.join("output", csv_file)) logging.info(deleted.format(csv_file)) xprint(f"{POSITIVE} {deleted.format(csv_file)}") @@ -103,11 +96,10 @@ def delete_csv(): # Clear csv files def clear_csv(): - xprint(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue? (yes/no) ", end="") - prompt = input().lower() + prompt = Prompt.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue?", choices=['yes', 'no']) if prompt == "yes": shutil.rmtree('output', ignore_errors=True) - xprint(f"{INFO} .csv files cleared successfully!") + xprint(f"{INFO} csv files cleared successfully!") else: pass @@ -129,8 +121,7 @@ def read_csv(): if args.csv_file: csv_file = args.csv_file else: - xprint(f"{green}csv {white}(filename.csv):{reset} ", end="") - csv_file = input() + csv_file = Prompt.ask(f"{green}csv {white}(filename){reset}") with open(os.path.join("output", csv_file), "r") as file: logging.info(reading.format(csv_file)) text = Text(file.read()) @@ -153,8 +144,8 @@ def view_logs(): def read_log(): if args.log_file: log_file = args.log_file - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() + else: + log_file = Prompt.ask(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM){reset}") with open(os.path.join(".logs", log_file + ".log"), "r") as log: logging.info(reading.format(log_file)) xprint("\n" + log.read()) @@ -165,8 +156,7 @@ def delete_log(): if args.log_file: log_file = args.log_file else: - xprint(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM):{reset} ", end="") - log_file = input() + log_file = Prompt.ask(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM){reset}") os.remove(os.path.join(".logs", log_file)) logging.info(deleted.format(log_file)) xprint(f"{POSITIVE} {deleted.format(log_file)}") @@ -174,8 +164,7 @@ def delete_log(): # Clear logs def clear_logs(): - xprint(f"{PROMPT} This will clear all {len(os.listdir('.logs'))} logs and close the current session, continue? (yes/no) ", end="") - prompt = input().lower() + prompt = Prompt.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} log files, continue?", choices=['yes', 'no']) if prompt == "yes": shutil.rmtree('.logs', ignore_errors=True) xprint(f"{INFO} .log files cleared successfully!") @@ -186,9 +175,8 @@ def clear_logs(): # Exit session def exit_session(): - xprint(f"{PROMPT} This will close the current session, continue? (yes/no) ", end="") - prompt = input().lower() - if prompt == "yes": + exit_prompt = Prompt.ask(f"{PROMPT} This will close the current session, continue?", choices=['yes', 'no']) + if exit_prompt == "yes": logging.info(session_closed.format(datetime.now())) xprint(f"{INFO} {session_closed.format(datetime.now())}") exit() @@ -226,8 +214,7 @@ GitHub REST API documentation: https://docs.github.com/rest class Octosuite: - def __init__(self, args): - self.args = args + def __init__(self): # API endpoint self.endpoint = 'https://api.github.com' @@ -531,10 +518,10 @@ class Octosuite: # Fetching organization info def org_profile(self): - if self.args.organization: - organization = self.args.organization + if args.organization: + organization = args.organization else: - organization = input("@Organization: ") + organization = Prompt.ask(f"{white}@{green}Organization{reset}") response = requests.get(f"{self.endpoint}/orgs/{organization}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -549,10 +536,10 @@ class Octosuite: # Fetching user information def user_profile(self): - if self.args.username: - username = self.args.username + if args.username: + username = args.username else: - username = input("@Username: ") + username = Prompt.ask(f"{white}@{green}Username{reset}") response = requests.get(f"{self.endpoint}/users/{username}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -561,19 +548,22 @@ class Octosuite: for attr in self.profile_attrs: user_profile_tree.add(f"{self.profile_attr_dict[attr]}: {response.json()[attr]}") xprint(user_profile_tree) - log_user_profile(response) + + # Logging output to a csv file + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_profile(response) else: xprint(response.json()) # Fetching repository information def repo_profile(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username else: - repo_name = input("%Repository: ") - username = input("@Username: ") - response = requests.get(f"{self.endpoint}/repos/{self.username}/{repo_name}") + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") elif response.status_code == 200: @@ -581,20 +571,22 @@ class Octosuite: for attr in self.repo_attrs: repo_profile_tree.add(f"{self.repo_attr_dict[attr]}: {response.json()[attr]}") xprint(repo_profile_tree) - log_repo_profile(response) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repo_profile(response) else: xprint(response.json()) # Get path contents def path_contents(self): - if self.args.repository and self.args.username and self.args.path_name: - repo_name = self.args.repository - username = self.args.username - path_name = self.args.path_name + if args.repository and args.username and args.path_name: + repo_name = args.repository + username = args.username + path_name = args.path_name else: - repo_name = input("%Repository: ") - username = input("@Username: ") - path_name = input("~/path/name ") + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + path_name = Prompt.ask("~/path/name ") response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/contents/{path_name}") if response.status_code == 404: xprint(f"{NEGATIVE} {info_not_found.format(repo_name, username, path_name)}") @@ -611,14 +603,14 @@ class Octosuite: # repo contributors def repo_contributors(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username - limit = self.args.limit + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username + limit = args.limit else: - repo_name = input("%Repository: ") - username = input("@Username: ") - limit = input(limit_output.format("contributors")) + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("contributors")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -628,20 +620,22 @@ class Octosuite: for attr in self.user_attrs: contributor_tree.add(f"{self.user_attr_dict[attr]}: {contributor[attr]}") xprint(contributor_tree) - log_repo_contributors(contributor, repo_name) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repo_contributors(contributor, repo_name) else: xprint(response.json()) # repo stargazers def repo_stargazers(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username - limit = self.args.limit + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username + limit = args.limit else: - repo_name = input("%Repository: ") - username = input("@Username: ") - limit = input(limit_output.format("stargazers")) + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("stargazers")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -653,20 +647,22 @@ class Octosuite: for attr in self.user_attrs: stargazer_tree.add(f"{self.user_attr_dict[attr]}: {stargazer[attr]}") xprint(stargazer_tree) - log_repo_stargazers(stargazer, repo_name) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repo_stargazers(stargazer, repo_name) else: xprint(response.json()) # repo forks def repo_forks(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username - limit = self.args.limit + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username + limit = args.limit else: - repo_name = input("%Repository: ") - username = input("@Username: ") - limit = input(limit_output.format("forks")) + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("forks")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -678,20 +674,22 @@ class Octosuite: for attr in self.repo_attrs: fork_tree.add(f"{self.repo_attr_dict[attr]}: {fork[attr]}") xprint(fork_tree) - log_repo_forks(fork, count) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repo_forks(fork, count) else: xprint(response.json()) # Repo issues def repo_issues(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username - limit = self.args.limit + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username + limit = args.limit else: - repo_name = input("%Repository: ") - username = input("@Username: ") - limit = input(limit_output.format("issues")) + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("issues")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -710,14 +708,14 @@ class Octosuite: # Repo releases def repo_releases(self): - if self.args.repository and self.args.username and self.args.limit: - repo_name = self.args.repository - username = self.args.username - limit = self.args.limit + if args.repository and args.username and args.limit: + repo_name = args.repository + username = args.username + limit = args.limit else: - repo_name = input("%Repository: ") - username = input("@Username: ") - limit = input(limit_output.format("repository releases")) + repo_name = Prompt.ask(f"{white}%{green}Repository{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("repository releases")) response = requests.get(f"{self.endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {repo_or_user_not_found.format(repo_name, username)}") @@ -730,18 +728,20 @@ class Octosuite: releases_tree.add(f"{self.repo_releases_attr_dict[attr]}: {release[attr]}") xprint(releases_tree) xprint(release['body']) - log_repo_releases(release, repo_name) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repo_releases(release, repo_name) else: xprint(response.json()) # Fetching organization repositories def org_repos(self): - if self.args.organization and self.args.limit: - organization = self.args.organization - limit = self.args.limit + if args.organization and args.limit: + organization = args.organization + limit = args.limit else: - organization = input("@Organization: ") - limit = input(limit_output.format("organization repositories")) + organization = Prompt.ask(f"{white}@{green}Organization{reset}") + limit = Prompt.ask(limit_output.format("organization repositories")) response = requests.get(f"{self.endpoint}/orgs/{organization}/repos?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -751,18 +751,20 @@ class Octosuite: for attr in self.repo_attrs: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - log_org_repos(repository, organization) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_org_repos(repository, organization) else: xprint(response.json()) # organization events def org_events(self): - if self.args.organization and self.args.limit: - organization = self.args.organization - limit = self.args.limit + if args.organization and args.limit: + organization = args.organization + limit = args.limit else: - organization = input("@Organization: ") - limit = input(limit_output.format("organization events")) + organization = Prompt.ask(f"{white}@{green}Organization{reset}") + limit = Prompt.ask(limit_output.format("organization events")) response = requests.get(f"{self.endpoint}/orgs/{organization}/events?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -779,12 +781,12 @@ class Octosuite: # organization member def org_member(self): - if self.args.organization and self.args.username: - organization = self.args.organization - username = self.args.username + if args.organization and args.username: + organization = args.organization + username = args.username else: - organization = input("@Organization: ") - username = input("@Username: ") + organization = Prompt.ask(f"{white}@{green}Organization{reset}") + username = Prompt.ask(f"{white}@{green}Username{reset}") response = requests.get(f"{self.endpoint}/orgs/{organization}/public_members/{username}") if response.status_code == 204: xprint(f"{POSITIVE} User ({username}) is a public member of the organization -> ({organization})") @@ -793,12 +795,12 @@ class Octosuite: # Fetching user repositories def user_repos(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("repositories")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("repositories")) response = requests.get(f"{self.endpoint}/users/{username}/repos?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -808,18 +810,20 @@ class Octosuite: for attr in self.repo_attrs: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - log_user_repos(repository, username) - else: - xprint(response.json()) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_repos(repository, username) + else: + xprint(response.json()) # Fetching user's gists def user_gists(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format('gists')) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format('gists')) response = requests.get(f"{self.endpoint}/users/{username}/gists?per_page={limit}") if not response.json(): xprint(f"{NEGATIVE} User does not have gists.") @@ -831,18 +835,20 @@ class Octosuite: for attr in self.gists_attrs: gists_tree.add(f"{self.gists_attr_dict[attr]}: {gist[attr]}") xprint(gists_tree) - log_user_gists(gist) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_gists(gist) else: xprint(response.json()) # Fetching a list of organizations that a user owns or belongs to def user_orgs(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("user organizations")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("user organizations")) response = requests.get(f"{self.endpoint}/users/{username}/orgs?per_page={limit}") if not response.json(): xprint(f"{NEGATIVE} User ({username}) does not (belong to/own) any organizations.") @@ -854,18 +860,20 @@ class Octosuite: for attr in self.user_orgs_attrs: org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organization[attr]}") xprint(org_tree) - log_user_orgs(organization, username) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_orgs(organization, username) else: xprint(response.json()) # Fetching a users events def user_events(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("events")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("events")) response = requests.get(f"{self.endpoint}/users/{username}/events/public?per_page={limit}") if response.status_code == 404: xprint(f"{NEGATIVE} {user_not_found.format(username)}") @@ -884,12 +892,12 @@ class Octosuite: # Fetching a target user's subscriptions def user_subscriptions(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("user subscriptions")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("user subscriptions")) response = requests.get(f"{self.endpoint}/users/{username}/subscriptions?per_page={limit}") if not response.json(): xprint(f"{NEGATIVE} User does not have any subscriptions.") @@ -901,18 +909,20 @@ class Octosuite: for attr in self.repo_attrs: subscriptions_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(subscriptions_tree) - log_user_subscriptions(repository, username) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_subscriptions(repository, username) else: xprint(response.json()) # Fetching a list of users the target follows def user_following(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("user' following")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("user' following")) response = requests.get(f"{self.endpoint}/users/{username}/following?per_page={limit}") if not response.json(): xprint(f"{NEGATIVE} User ({username})does not follow anyone.") @@ -924,18 +934,20 @@ class Octosuite: for attr in self.user_attrs: following_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(following_tree) - log_user_following(user, username) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_following(user, username) else: xprint(response.json()) # Fetching user's followers def user_followers(self): - if self.args.username and self.args.limit: - username = self.args.username - limit = self.args.limit + if args.username and args.limit: + username = args.username + limit = args.limit else: - username = input("@Username: ") - limit = input(limit_output.format("user followers")) + username = Prompt.ask(f"{white}@{green}Username{reset}") + limit = Prompt.ask(limit_output.format("user followers")) response = requests.get(f"{self.endpoint}/users/{username}/followers?per_page={limit}") if not response.json(): xprint(f"{NEGATIVE} User ({username})does not have followers.") @@ -947,18 +959,20 @@ class Octosuite: for attr in self.user_attrs: followers_tree.add(f"{self.user_attr_dict[attr]}: {follower[attr]}") xprint(followers_tree) - log_user_followers(follower, username) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_user_followers(follower, username) else: xprint(response.json()) # Checking whether user[A] follows user[B] def user_follows(self): - if self.args.username and self.args.username_b: - user_a = self.args.username - user_b = self.args.username_b + if args.username and args.username_b: + user_a = args.username + user_b = args.username_b else: - user_a = input("@User_A: ") - user_b = input("@User_B") + user_a = Prompt.ask(f"{white}@{green}User_A{reset}") + user_b = Prompt.ask(f"{white}@{green}User_B{reset}") response = requests.get(f"{self.endpoint}/users/{user_a}/following/{user_b}") if response.status_code == 204: xprint(f"{POSITIVE} @{user_a} FOLLOWS @{user_b}") @@ -967,60 +981,66 @@ class Octosuite: # User search def users_search(self): - if self.args.query and self.args.limit and self.args.limit: - query = self.args.query - limit = self.args.limit + if args.query and args.limit and args.limit: + query = args.query + limit = args.limit else: - query = input("@Username (search): ") - limit = input(limit_output.format("user search")) + query = Prompt.ask(f"{white}@{green}Username{reset} (search)") + limit = Prompt.ask(limit_output.format("user search")) response = requests.get(f"{self.endpoint}/search/users?q={query}&per_page={limit}").json() for user in response['items']: users_search_tree = Tree("\n" + user['login']) for attr in self.user_attrs: users_search_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(users_search_tree) - log_users_search(user, query) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_users_search(user, query) # Repository search def repos_search(self): - if self.args.query and self.args.limit: - query = self.args.query - limit = self.args.limit + if args.query and args.limit: + query = args.query + limit = args.limit else: - query = input("%Repository (search): ") - limit = input(limit_output.format("repositor[y][ies] search")) + query = Prompt.ask(f"{white}%{green}Repository{reset} (search)") + limit = Prompt.ask(limit_output.format("repositor[y][ies] search")) response = requests.get(f"{self.endpoint}/search/repositories?q={query}&per_page={limit}").json() for repository in response['items']: repos_search_tree = Tree("\n" + repository['full_name']) for attr in self.repo_attrs: repos_search_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_search_tree) - log_repos_search(repository, query) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_repos_search(repository, query) # Topics search def topics_search(self): - if self.args.query and self.args.limit: - query = self.args.query - limit = self.args.limit + if args.query and args.limit: + query = args.query + limit = args.limit else: - query = input(":Topic (search): ") - limit = input(limit_output.format("topic(s) search")) + query = Prompt.ask(f"{white}:{green}Topics{reset} (search)") + limit = Prompt.ask(limit_output.format("topic(s) search")) response = requests.get(f"{self.endpoint}/search/topics?q={query}&per_page={limit}").json() for topic in response['items']: topics_search_tree = Tree("\n" + topic['name']) for attr in self.topic_attrs: topics_search_tree.add(f"{self.topic_attr_dict[attr]}: {topic[attr]}") xprint(topics_search_tree) - log_topics_search(topic, query) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_topics_search(topic, query) # Issue search def issues_search(self): - if self.args.query and self.args.limit: - query = self.args.query - limit = self.args.limit + if args.query and args.limit: + query = args.query + limit = args.limit else: - query = input("!Issue (search): ") - limit = input(limit_output.format("issue(s) search")) + query = Prompt.ask(f"{white}!{green}Issues{reset} (search)") + limit = Prompt.ask(limit_output.format("issue(s) search")) response = requests.get(f"{self.endpoint}/search/issues?q={query}&per_page={limit}").json() for issue in response['items']: issues_search_tree = Tree("\n" + issue['title']) @@ -1028,16 +1048,18 @@ class Octosuite: issues_search_tree.add(f"{self.repo_issues_attr_dict[attr]}: {issue[attr]}") xprint(issues_search_tree) xprint(issue['body']) - log_issues_search(issue, query) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_issues_search(issue, query) # Commits search def commits_search(self): - if self.args.query and self.args.limit: - query = self.args.query - limit = self.args.limit + if args.query and args.limit: + query = args.query + limit = args.limit else: - query = input(";Commit (search): ") - limit = input(limit_output.format("commit(s) search")) + query = Prompt.ask(f"{white};{green}Commits{reset} (search)") + limit = Prompt.ask(limit_output.format("commit(s) search")) response = requests.get(f"{self.endpoint}/search/commits?q={query}&per_page={limit}").json() for commit in response['items']: commits_search_tree = Tree("\n" + commit['commit']['tree']['sha']) @@ -1049,7 +1071,9 @@ class Octosuite: commits_search_tree.add(f"URL: {commit['html_url']}") xprint(commits_search_tree) xprint(commit['commit']['message']) - log_commits_search(commit, query) + + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + log_commits_search(commit, query) # Downloading release tarball def download_tarball(self): @@ -1063,7 +1087,7 @@ class Octosuite: logging.info(file_downloaded.format(f"octosuite.v{version_tag}.tar")) xprint(POSITIVE, file_downloaded.format(f"octosuite.v{version_tag}.tar")) - # Downloading release zipball + # Downloading release zip ball def download_zipball(self): logging.info(file_downloading.format(f"octosuite.v{version_tag}.zip")) xprint(INFO, file_downloading.format(f"octosuite.v{version_tag}.zip")) From bb82427abe7a866c98ea47cf3a8248907113510f Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 02:58:05 +0200 Subject: [PATCH 40/51] Update csv_loggers.py --- octosuite/csv_loggers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/octosuite/csv_loggers.py b/octosuite/csv_loggers.py index ef39394..c518b82 100644 --- a/octosuite/csv_loggers.py +++ b/octosuite/csv_loggers.py @@ -2,7 +2,7 @@ import os import csv import logging from rich import print as xprint -from octosuite.log_roller import prompt_log_csv, logged_to_csv, logging_skipped +from octosuite.log_roller import prompt_log_csv, logged_to_csv from octosuite.message_prefixes import PROMPT, WARNING, POSITIVE, NEGATIVE, INFO @@ -440,4 +440,3 @@ def log_commits_search(commit, query): logging.info(logged_to_csv.format(file.name)) xprint(f"{POSITIVE} {logged_to_csv.format(file.name)}") - From 53ccc9899b3ddf5fc71c135db505d03a68a0e243 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 03:02:42 +0200 Subject: [PATCH 41/51] Update main.py --- octosuite/main.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/octosuite/main.py b/octosuite/main.py index b09bd73..f456271 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -18,29 +18,31 @@ def octosuite(): if args.method == argument: method() print("\n") - - """ - Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) - or if the 'exit' command is entered. - """ - xprint(banner()[0], banner()[1]) - while True: - command_input = Prompt.ask(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}") + else: + pass + else: """ - Iterate over the command_map and check if the user input matches any command in it [command_map], - if there's a match, we return its method. If no match is found, we ignore it. + Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C) + or if the 'exit' command is entered. """ - if command_input[:2] == 'cd': - os.chdir(command_input[3:]) - elif command_input[:2] == 'ls': - os.system(f'dir {command_input[3:]}' if os.name == 'nt' else f'ls {command_input[3:]}') - else: - for command, method in run.command_map: - if command_input == command: - method() - print("\n") - else: - pass + xprint(banner()[0], banner()[1]) + while True: + command_input = Prompt.ask(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}") + """ + Iterate over the command_map and check if the user input matches any command in it [command_map], + if there's a match, we return its method. If no match is found, we ignore it. + """ + if command_input[:2] == 'cd': + os.chdir(command_input[3:]) + elif command_input[:2] == 'ls': + os.system(f'dir {command_input[3:]}' if os.name == 'nt' else f'ls {command_input[3:]}') + else: + for command, method in run.command_map: + if command_input == command: + method() + print("\n") + else: + pass except KeyboardInterrupt: logging.warning(ctrl_c) From 96b10f0bf99ea4a4784d65856fce0bf6c150d24c Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 03:09:29 +0200 Subject: [PATCH 42/51] Update main.py --- octosuite/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/main.py b/octosuite/main.py index f456271..7907c5f 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -9,7 +9,7 @@ def octosuite(): clear_screen() configure_logging() check_updates() - if args: + if args.method: """ Iterate over the argument_map and check if the passed command line argument matches any argument in it [argument_map], if there's a match, we return its method. If no match is found, we do nothing (which will return the usage). From 48b86cb2d04dfa7406a25afbe5609d08abcdb846 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 03:12:12 +0200 Subject: [PATCH 43/51] Update octosuite.py --- octosuite/octosuite.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index e6fd82c..8cacb6c 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -550,7 +550,7 @@ class Octosuite: xprint(user_profile_tree) # Logging output to a csv file - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_profile(response) else: xprint(response.json()) @@ -572,7 +572,7 @@ class Octosuite: repo_profile_tree.add(f"{self.repo_attr_dict[attr]}: {response.json()[attr]}") xprint(repo_profile_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_profile(response) else: xprint(response.json()) @@ -621,7 +621,7 @@ class Octosuite: contributor_tree.add(f"{self.user_attr_dict[attr]}: {contributor[attr]}") xprint(contributor_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_contributors(contributor, repo_name) else: xprint(response.json()) @@ -648,7 +648,7 @@ class Octosuite: stargazer_tree.add(f"{self.user_attr_dict[attr]}: {stargazer[attr]}") xprint(stargazer_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_stargazers(stargazer, repo_name) else: xprint(response.json()) @@ -675,7 +675,7 @@ class Octosuite: fork_tree.add(f"{self.repo_attr_dict[attr]}: {fork[attr]}") xprint(fork_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_forks(fork, count) else: xprint(response.json()) @@ -729,7 +729,7 @@ class Octosuite: xprint(releases_tree) xprint(release['body']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_releases(release, repo_name) else: xprint(response.json()) @@ -752,7 +752,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_org_repos(repository, organization) else: xprint(response.json()) @@ -811,7 +811,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_repos(repository, username) else: xprint(response.json()) @@ -836,7 +836,7 @@ class Octosuite: gists_tree.add(f"{self.gists_attr_dict[attr]}: {gist[attr]}") xprint(gists_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_gists(gist) else: xprint(response.json()) @@ -861,7 +861,7 @@ class Octosuite: org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organization[attr]}") xprint(org_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_orgs(organization, username) else: xprint(response.json()) @@ -910,7 +910,7 @@ class Octosuite: subscriptions_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(subscriptions_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_subscriptions(repository, username) else: xprint(response.json()) @@ -935,7 +935,7 @@ class Octosuite: following_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(following_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_following(user, username) else: xprint(response.json()) @@ -960,7 +960,7 @@ class Octosuite: followers_tree.add(f"{self.user_attr_dict[attr]}: {follower[attr]}") xprint(followers_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_followers(follower, username) else: xprint(response.json()) @@ -994,7 +994,7 @@ class Octosuite: users_search_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(users_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_users_search(user, query) # Repository search @@ -1012,7 +1012,7 @@ class Octosuite: repos_search_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repos_search(repository, query) # Topics search @@ -1030,7 +1030,7 @@ class Octosuite: topics_search_tree.add(f"{self.topic_attr_dict[attr]}: {topic[attr]}") xprint(topics_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_topics_search(topic, query) # Issue search @@ -1049,7 +1049,7 @@ class Octosuite: xprint(issues_search_tree) xprint(issue['body']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_issues_search(issue, query) # Commits search @@ -1072,7 +1072,7 @@ class Octosuite: xprint(commits_search_tree) xprint(commit['commit']['message']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_commits_search(commit, query) # Downloading release tarball From 866621db189d95be3c06cda10d9358701f98c710 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 03:25:43 +0200 Subject: [PATCH 44/51] Update octosuite.py --- octosuite/octosuite.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index 8cacb6c..e6fd82c 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -550,7 +550,7 @@ class Octosuite: xprint(user_profile_tree) # Logging output to a csv file - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_profile(response) else: xprint(response.json()) @@ -572,7 +572,7 @@ class Octosuite: repo_profile_tree.add(f"{self.repo_attr_dict[attr]}: {response.json()[attr]}") xprint(repo_profile_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_profile(response) else: xprint(response.json()) @@ -621,7 +621,7 @@ class Octosuite: contributor_tree.add(f"{self.user_attr_dict[attr]}: {contributor[attr]}") xprint(contributor_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_contributors(contributor, repo_name) else: xprint(response.json()) @@ -648,7 +648,7 @@ class Octosuite: stargazer_tree.add(f"{self.user_attr_dict[attr]}: {stargazer[attr]}") xprint(stargazer_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_stargazers(stargazer, repo_name) else: xprint(response.json()) @@ -675,7 +675,7 @@ class Octosuite: fork_tree.add(f"{self.repo_attr_dict[attr]}: {fork[attr]}") xprint(fork_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_forks(fork, count) else: xprint(response.json()) @@ -729,7 +729,7 @@ class Octosuite: xprint(releases_tree) xprint(release['body']) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repo_releases(release, repo_name) else: xprint(response.json()) @@ -752,7 +752,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_org_repos(repository, organization) else: xprint(response.json()) @@ -811,7 +811,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_repos(repository, username) else: xprint(response.json()) @@ -836,7 +836,7 @@ class Octosuite: gists_tree.add(f"{self.gists_attr_dict[attr]}: {gist[attr]}") xprint(gists_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_gists(gist) else: xprint(response.json()) @@ -861,7 +861,7 @@ class Octosuite: org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organization[attr]}") xprint(org_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_orgs(organization, username) else: xprint(response.json()) @@ -910,7 +910,7 @@ class Octosuite: subscriptions_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(subscriptions_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_subscriptions(repository, username) else: xprint(response.json()) @@ -935,7 +935,7 @@ class Octosuite: following_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(following_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_following(user, username) else: xprint(response.json()) @@ -960,7 +960,7 @@ class Octosuite: followers_tree.add(f"{self.user_attr_dict[attr]}: {follower[attr]}") xprint(followers_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_user_followers(follower, username) else: xprint(response.json()) @@ -994,7 +994,7 @@ class Octosuite: users_search_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(users_search_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_users_search(user, query) # Repository search @@ -1012,7 +1012,7 @@ class Octosuite: repos_search_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_search_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_repos_search(repository, query) # Topics search @@ -1030,7 +1030,7 @@ class Octosuite: topics_search_tree.add(f"{self.topic_attr_dict[attr]}: {topic[attr]}") xprint(topics_search_tree) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_topics_search(topic, query) # Issue search @@ -1049,7 +1049,7 @@ class Octosuite: xprint(issues_search_tree) xprint(issue['body']) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_issues_search(issue, query) # Commits search @@ -1072,7 +1072,7 @@ class Octosuite: xprint(commits_search_tree) xprint(commit['commit']['message']) - if args.log_csv == "yes" or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": log_commits_search(commit, query) # Downloading release tarball From 9982727de190795c4e381bc42a781593cf656a05 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sun, 25 Dec 2022 03:27:55 +0200 Subject: [PATCH 45/51] Update config.py --- octosuite/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index 8f5fab8..e62d273 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -1,7 +1,6 @@ import psutil import platform import argparse - from rich.tree import Tree from rich.text import Text from rich.table import Table @@ -142,7 +141,7 @@ def create_parser(): parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') parser.add_argument('--csv_file', help='specify a csv file (used with csv management methods)') parser.add_argument('--log_file', help='specify a log file (used with logs management methods)') - parser.add_argument('--log_csv', help='specify to log output to a csv', action='store_true') + parser.add_argument('--log_csv', help='log output to a csv (default: %(default)s)', choices=['yes', 'no'], default='no') return parser From 4427f30f77381f91bd5c5493494d71f06b80f2f0 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Tue, 27 Dec 2022 19:12:14 +0200 Subject: [PATCH 46/51] Update config.py --- octosuite/config.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index e62d273..e4f00c1 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -84,12 +84,12 @@ def usage(): Read log -------- - octosuite --method read_log --log_file + octosuite --method read_log --log-file Delete log ---------- - octosuite --method delete_log --log_file + octosuite --method delete_log --log-file Clear logs @@ -108,12 +108,12 @@ def usage(): Read CSV -------- - octosuite --method read_csv --csv_file + octosuite --method read_csv --csv-file Delete CSV ---------- - octosuite --method delete_csv --csv_file + octosuite --method delete_csv --csv-file Clear CSV's @@ -139,9 +139,9 @@ def create_parser(): parser.add_argument('-q', '--query', help='query (used with search methods)') parser.add_argument('-l', '--limit', help='output limit (used with methods that return results in bulk) (default: %(default)s)', default=10) parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') - parser.add_argument('--csv_file', help='specify a csv file (used with csv management methods)') - parser.add_argument('--log_file', help='specify a log file (used with logs management methods)') - parser.add_argument('--log_csv', help='log output to a csv (default: %(default)s)', choices=['yes', 'no'], default='no') + parser.add_argument('--csv-file', help='specify a csv file (used with csv management methods)', dest='csv_file') + parser.add_argument('--log-file', help='specify a log file (used with logs management methods)', dest='log_file') + parser.add_argument('--log-to-csv', help='log output to a csv (default: %(default)s)', choices=['yes', 'no'], default='no') return parser From 6a7b61b1f22a33966711f2cbc4ba5414772e7a50 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:47:48 +0200 Subject: [PATCH 47/51] Update octosuite.py --- octosuite/octosuite.py | 70 ++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index e6fd82c..37a3626 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -10,7 +10,7 @@ import platform import subprocess from datetime import datetime from octosuite.banner import version_tag, banner -from octosuite.config import Tree, Text, Table, Prompt, xprint, create_parser, args, red, white, green, yellow, header_title, reset +from octosuite.config import Tree, Text, Table, Prompt, Confirm, xprint, create_parser, args, red, white, green, yellow, header_title, reset from octosuite.message_prefixes import ERROR, WARNING, PROMPT, POSITIVE, NEGATIVE, INFO # wondering why I name all the variables instead of just using the * wildcard?, because it's the pythonic way lol # seriously now, the reason why I am doing this, is so that you know exactly what I am importing from a named module :) from octosuite.helper import help_command, source_command, search_command, user_command, repo_command, \ @@ -88,7 +88,7 @@ def delete_csv(): if args.csv_file: csv_file = args.csv_file else: - csv_file = Prompt.ask(f"{green}csv {white}(filename){reset}") + csv_file = Prompt.ask(f"{green}.csv {white}(filename){reset}") os.remove(os.path.join("output", csv_file)) logging.info(deleted.format(csv_file)) xprint(f"{POSITIVE} {deleted.format(csv_file)}") @@ -96,8 +96,8 @@ def delete_csv(): # Clear csv files def clear_csv(): - prompt = Prompt.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue?", choices=['yes', 'no']) - if prompt == "yes": + clear_csv_prompt = Confirm.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} csv files, continue?") + if clear_csv_prompt: shutil.rmtree('output', ignore_errors=True) xprint(f"{INFO} csv files cleared successfully!") else: @@ -121,7 +121,7 @@ def read_csv(): if args.csv_file: csv_file = args.csv_file else: - csv_file = Prompt.ask(f"{green}csv {white}(filename){reset}") + csv_file = Prompt.ask(f"{green}.csv {white}(filename){reset}") with open(os.path.join("output", csv_file), "r") as file: logging.info(reading.format(csv_file)) text = Text(file.read()) @@ -145,7 +145,7 @@ def read_log(): if args.log_file: log_file = args.log_file else: - log_file = Prompt.ask(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM){reset}") + log_file = Prompt.ask(f"{green}.log date{white} (eg. 2022-04-27 10:09:36AM){reset}") with open(os.path.join(".logs", log_file + ".log"), "r") as log: logging.info(reading.format(log_file)) xprint("\n" + log.read()) @@ -156,7 +156,7 @@ def delete_log(): if args.log_file: log_file = args.log_file else: - log_file = Prompt.ask(f"{green}log date{white} (eg. 2022-04-27 10:09:36AM){reset}") + log_file = Prompt.ask(f"{green}.log date{white} (eg. 2022-04-27 10:09:36AM){reset}") os.remove(os.path.join(".logs", log_file)) logging.info(deleted.format(log_file)) xprint(f"{POSITIVE} {deleted.format(log_file)}") @@ -164,8 +164,8 @@ def delete_log(): # Clear logs def clear_logs(): - prompt = Prompt.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} log files, continue?", choices=['yes', 'no']) - if prompt == "yes": + clear_logs_prompt = Confirm.ask(f"{PROMPT} This will clear all {len(os.listdir('output'))} log files, continue?") + if clear_logs_prompt: shutil.rmtree('.logs', ignore_errors=True) xprint(f"{INFO} .log files cleared successfully!") exit() @@ -175,8 +175,8 @@ def clear_logs(): # Exit session def exit_session(): - exit_prompt = Prompt.ask(f"{PROMPT} This will close the current session, continue?", choices=['yes', 'no']) - if exit_prompt == "yes": + exit_prompt = Confirm.ask(f"{PROMPT} This will close the current session, continue?") + if exit_prompt: logging.info(session_closed.format(datetime.now())) xprint(f"{INFO} {session_closed.format(datetime.now())}") exit() @@ -521,7 +521,7 @@ class Octosuite: if args.organization: organization = args.organization else: - organization = Prompt.ask(f"{white}@{green}Organization{reset}") + organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}") response = requests.get(f"{self.endpoint}/orgs/{organization}") if response.status_code == 404: xprint(f"{NEGATIVE} {org_not_found.format(organization)}") @@ -530,7 +530,9 @@ class Octosuite: for attr in self.org_attrs: org_profile_tree.add(f"{self.org_attr_dict[attr]}: {response.json()[attr]}") xprint(org_profile_tree) - log_org_profile(response) + + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): + log_org_profile(response) else: xprint(response.json()) @@ -550,7 +552,7 @@ class Octosuite: xprint(user_profile_tree) # Logging output to a csv file - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_profile(response) else: xprint(response.json()) @@ -572,7 +574,7 @@ class Octosuite: repo_profile_tree.add(f"{self.repo_attr_dict[attr]}: {response.json()[attr]}") xprint(repo_profile_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repo_profile(response) else: xprint(response.json()) @@ -621,7 +623,7 @@ class Octosuite: contributor_tree.add(f"{self.user_attr_dict[attr]}: {contributor[attr]}") xprint(contributor_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repo_contributors(contributor, repo_name) else: xprint(response.json()) @@ -648,7 +650,7 @@ class Octosuite: stargazer_tree.add(f"{self.user_attr_dict[attr]}: {stargazer[attr]}") xprint(stargazer_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repo_stargazers(stargazer, repo_name) else: xprint(response.json()) @@ -675,7 +677,7 @@ class Octosuite: fork_tree.add(f"{self.repo_attr_dict[attr]}: {fork[attr]}") xprint(fork_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repo_forks(fork, count) else: xprint(response.json()) @@ -729,7 +731,7 @@ class Octosuite: xprint(releases_tree) xprint(release['body']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repo_releases(release, repo_name) else: xprint(response.json()) @@ -740,7 +742,7 @@ class Octosuite: organization = args.organization limit = args.limit else: - organization = Prompt.ask(f"{white}@{green}Organization{reset}") + organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}") limit = Prompt.ask(limit_output.format("organization repositories")) response = requests.get(f"{self.endpoint}/orgs/{organization}/repos?per_page={limit}") if response.status_code == 404: @@ -752,7 +754,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}") == "yes": log_org_repos(repository, organization) else: xprint(response.json()) @@ -763,7 +765,7 @@ class Octosuite: organization = args.organization limit = args.limit else: - organization = Prompt.ask(f"{white}@{green}Organization{reset}") + organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}") limit = Prompt.ask(limit_output.format("organization events")) response = requests.get(f"{self.endpoint}/orgs/{organization}/events?per_page={limit}") if response.status_code == 404: @@ -785,7 +787,7 @@ class Octosuite: organization = args.organization username = args.username else: - organization = Prompt.ask(f"{white}@{green}Organization{reset}") + organization = Prompt.ask(f"{white}@{green}Organi[sz]ation{reset}") username = Prompt.ask(f"{white}@{green}Username{reset}") response = requests.get(f"{self.endpoint}/orgs/{organization}/public_members/{username}") if response.status_code == 204: @@ -811,7 +813,7 @@ class Octosuite: repos_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_repos(repository, username) else: xprint(response.json()) @@ -836,7 +838,7 @@ class Octosuite: gists_tree.add(f"{self.gists_attr_dict[attr]}: {gist[attr]}") xprint(gists_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_gists(gist) else: xprint(response.json()) @@ -861,7 +863,7 @@ class Octosuite: org_tree.add(f"{self.user_orgs_attr_dict[attr]}: {organization[attr]}") xprint(org_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_orgs(organization, username) else: xprint(response.json()) @@ -910,7 +912,7 @@ class Octosuite: subscriptions_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(subscriptions_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_subscriptions(repository, username) else: xprint(response.json()) @@ -935,7 +937,7 @@ class Octosuite: following_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(following_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_following(user, username) else: xprint(response.json()) @@ -960,7 +962,7 @@ class Octosuite: followers_tree.add(f"{self.user_attr_dict[attr]}: {follower[attr]}") xprint(followers_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_user_followers(follower, username) else: xprint(response.json()) @@ -994,7 +996,7 @@ class Octosuite: users_search_tree.add(f"{self.user_attr_dict[attr]}: {user[attr]}") xprint(users_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_users_search(user, query) # Repository search @@ -1012,7 +1014,7 @@ class Octosuite: repos_search_tree.add(f"{self.repo_attr_dict[attr]}: {repository[attr]}") xprint(repos_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_repos_search(repository, query) # Topics search @@ -1030,7 +1032,7 @@ class Octosuite: topics_search_tree.add(f"{self.topic_attr_dict[attr]}: {topic[attr]}") xprint(topics_search_tree) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_topics_search(topic, query) # Issue search @@ -1049,7 +1051,7 @@ class Octosuite: xprint(issues_search_tree) xprint(issue['body']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_issues_search(issue, query) # Commits search @@ -1072,7 +1074,7 @@ class Octosuite: xprint(commits_search_tree) xprint(commit['commit']['message']) - if args.log_csv or Prompt.ask(f"{PROMPT} {prompt_log_csv}", choices=['yes', 'no']) == "yes": + if args.log_csv or Confirm.ask(f"\n{PROMPT} {prompt_log_csv}"): log_commits_search(commit, query) # Downloading release tarball From c2a5dc89ea7c6f22ab503f52a5a4a6c693c7bf4a Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:49:26 +0200 Subject: [PATCH 48/51] Update main.py --- octosuite/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/main.py b/octosuite/main.py index 7907c5f..6b6eee6 100644 --- a/octosuite/main.py +++ b/octosuite/main.py @@ -27,7 +27,7 @@ def octosuite(): """ xprint(banner()[0], banner()[1]) while True: - command_input = Prompt.ask(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}") + command_input = Prompt.ask(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼{reset}") """ Iterate over the command_map and check if the user input matches any command in it [command_map], if there's a match, we return its method. If no match is found, we ignore it. From f60f088f05d5b2b2019b6631cc852133113cbfb5 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:50:37 +0200 Subject: [PATCH 49/51] Update log_roller.py --- octosuite/log_roller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octosuite/log_roller.py b/octosuite/log_roller.py index d47b8af..216b965 100644 --- a/octosuite/log_roller.py +++ b/octosuite/log_roller.py @@ -16,6 +16,6 @@ info_not_found = "Information not found: {}, {}, {}" user_not_found = "User not found: @{}" org_not_found = "Organization not found: @{}" repo_or_user_not_found = "Repository or User not found: {}, @{}" -prompt_log_csv = "Do you wish to log this output to a CSV file?" +prompt_log_csv = "Would you like to log this output to a .csv file?" logged_to_csv = "Output logged: {}" limit_output = "Limit '{}' output to how many? (1-100)" From a14b356b186790a9804258c59c58c6c4081ffd38 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:52:02 +0200 Subject: [PATCH 50/51] Update config.py --- octosuite/config.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/octosuite/config.py b/octosuite/config.py index e4f00c1..c7a6cc6 100644 --- a/octosuite/config.py +++ b/octosuite/config.py @@ -5,8 +5,8 @@ from rich.tree import Tree from rich.text import Text from rich.table import Table from datetime import datetime -from rich.prompt import Prompt from rich import print as xprint +from rich.prompt import Prompt, Confirm def usage(): @@ -84,12 +84,12 @@ def usage(): Read log -------- - octosuite --method read_log --log-file + octosuite --method read_log --log_file Delete log ---------- - octosuite --method delete_log --log-file + octosuite --method delete_log --log_file Clear logs @@ -108,12 +108,12 @@ def usage(): Read CSV -------- - octosuite --method read_csv --csv-file + octosuite --method read_csv --csv_file Delete CSV ---------- - octosuite --method delete_csv --csv-file + octosuite --method delete_csv --csv_file Clear CSV's @@ -139,9 +139,9 @@ def create_parser(): parser.add_argument('-q', '--query', help='query (used with search methods)') parser.add_argument('-l', '--limit', help='output limit (used with methods that return results in bulk) (default: %(default)s)', default=10) parser.add_argument('-c', '--colors', '--colours', help='specify to run octosuite cli with colo[u]rs enabled', action='store_true') - parser.add_argument('--csv-file', help='specify a csv file (used with csv management methods)', dest='csv_file') - parser.add_argument('--log-file', help='specify a log file (used with logs management methods)', dest='log_file') - parser.add_argument('--log-to-csv', help='log output to a csv (default: %(default)s)', choices=['yes', 'no'], default='no') + parser.add_argument('--csv_file', help='csv file (used with csv management methods)') + parser.add_argument('--log_file', help='log file (used with logs management methods)') + parser.add_argument('--log-to-csv', help='log output to a csv file', action='store_true', dest='log_csv') return parser @@ -182,8 +182,8 @@ else: xprint(system_tree) print("\n") try: - color_chooser = Prompt.ask(f"Welcome, would you like to enable colo(u)rs for this session?", choices=['yes', 'no']) - if color_chooser == "yes": + color_chooser = Confirm.ask(f"Welcome, would you like to enable colo(u)rs for this session?") + if color_chooser: header_title = "bold white" red = "[red]" white = "[white]" From 8424c94becefd1a8307b20858ba45175c92279e4 Mon Sep 17 00:00:00 2001 From: Richard Mwewa <74001397+rly0nheart@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:56:16 +0200 Subject: [PATCH 51/51] Update Dockerfile --- Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index e6fcb16..8155b79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,6 @@ WORKDIR /app COPY . . -RUN pip install --upgrade pip -RUN pip install build -RUN python -m build -RUN pip install dist/*.whl +RUN pip install --upgrade pip && pip install build && python -m build && pip install dist/*.whl ENTRYPOINT ["octosuite"]