diff --git a/Dockerfile b/Dockerfile index d6c17aa..8155b79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ WORKDIR /app COPY . . -RUN pip install --upgrade pip && pip install build && 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"] 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) diff --git a/octosuite/banner.py b/octosuite/banner.py new file mode 100644 index 0000000..1573a92 --- /dev/null +++ b/octosuite/banner.py @@ -0,0 +1,23 @@ +import getpass +from octosuite.config import red, white, green, reset, Tree + + +# 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 - - - -""" - 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..c7a6cc6 --- /dev/null +++ b/octosuite/config.py @@ -0,0 +1,200 @@ +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 import print as xprint +from rich.prompt import Prompt, Confirm + + +def usage(): + return """ + Basic Usage + =========== + + Get User Profile Info + --------------------- + octosuite --method user_profile --username + + + Get User Repos + -------------- + octosuite --method user_repos --username + + + Get Organi[sz]ation Profile Info + ----------------------------- + octosuite --method org_profile --organization + + + Get Organi[sz]ation Repos + ----------------------------- + octosuite --method org_repos --organization + + + Get Repo Profile Info + --------------------- + octosuite --method repo_profile --username --repository + + + Get Repo Forks + -------------- + octosuite --method repo_forks --username --repository + + + + Searching + ========= + + Search Users + ------------ + octosuite --method users_search --query + + + Search Issues + ------------- + octosuite --method issues_search --query + + + Search Commits + -------------- + octosuite --method commits_search --query + + + Search Topics + ------------- + octosuite --method topics_search --query + + + Search Repositories + ------------------- + 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', '--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', '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 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='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 + + +parser = create_parser() +args = parser.parse_args() + +# 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()), + ("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]" + yellow = "[yellow]" + 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 = 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]" + 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 = yellow = "" + except KeyboardInterrupt: + exit(f"[WARNING] Process interrupted with Ctrl+C.") + 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)}") - diff --git a/octosuite/helper.py b/octosuite/helper.py index 9c0c10e..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.colors 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. @@ -146,6 +145,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 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") diff --git a/octosuite/log_roller.py b/octosuite/log_roller.py index c143844..216b965 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?" +prompt_log_csv = "Would you like 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)" diff --git a/octosuite/main.py b/octosuite/main.py index b09bd73..6b6eee6 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). @@ -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) diff --git a/octosuite/octosuite.py b/octosuite/octosuite.py index d248ccc..37a3626 100644 --- a/octosuite/octosuite.py +++ b/octosuite/octosuite.py @@ -10,14 +10,14 @@ 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, \ 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, prompt_log_csv, logging_skipped + 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, \ @@ -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