Compare commits

..

3 Commits
2.2.2 ... 2.2.3

Author SHA1 Message Date
Richard Mwewa
c296f09e0e Fixed issues that affected Windows machines
[Errno 22] Invalid argument: This error occurred in Windows machines on session start up, the datetime format used a symbol that is reserved by the windows system, thus breaking octosuite whenever it attempted to log an activity to a file.

[Error 2] The system cannot find the file specified: This error occurred whenever the clear command was entered, since the command was being passed through subprocess with shell set to False.
2022-09-14 18:34:04 +02:00
Richard Mwewa
04ea880276 Update README.md 2022-09-14 18:05:42 +02:00
Richard Mwewa
eef44716d5 Update setup.py 2022-09-01 11:53:35 +02:00
9 changed files with 494 additions and 412 deletions

View File

@@ -49,15 +49,13 @@
- [x] ...And more - [x] ...And more
## Note ## Note
> octosuite automatically logs network and minor user activity of each session. The logs are saved by date and time in the .logs folder > Octosuite automatically logs network and user activity of each session, the logs are saved by date and time in the .logs folder
>> If you believe octosuite can be better, feel free to open a pull request with your improvements✌🏾🙂
## License ## License
![license](https://user-images.githubusercontent.com/74001397/137917929-2f2cdb0c-4d1d-4e4b-9f0d-e01589e027b5.png) ![license](https://user-images.githubusercontent.com/74001397/137917929-2f2cdb0c-4d1d-4e4b-9f0d-e01589e027b5.png)
## Donations ## Donations
Love octosuite and would like to donate? You can buy me a coffee using the button below. Buy a coffee to the creator of *Octosuite*
<a href="https://www.buymeacoffee.com/189381184" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a> <a href="https://www.buymeacoffee.com/189381184" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
Your support is much appreciated!☕👌🏾😊

View File

@@ -1,11 +1,11 @@
import getpass import getpass
from octosuite.colors import red, white, green, reset from octosuite.colors import red, white, green, reset
''' """
banner.py banner.py
This file holds the program's banner logo and version tag This file holds the program's banner logo and version tag
''' """
version_tag = "2.2.2" version_tag = "2.2.3"
name_logo = f"""{white} name_logo = f"""{white}
_______ __ _______ __ __ _______ __ _______ __ __
| |.----.| |_.-----.| __|.--.--.|__| |_.-----. | |.----.| |_.-----.| __|.--.--.|__| |_.-----.

View File

@@ -5,39 +5,39 @@ from datetime import datetime
# This file gets called first at start up before any other file gets called # 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 # colors.py is the reason why users get to choose whether to enable/disable colors
system_info = [("Processor",platform.processor), system_info = [("Processor",platform.processor),
("Node", platform.node), ("Node", platform.node),
("Release", platform.release), ("Release", platform.release),
("Architecture", platform.architecture), ("Architecture", platform.architecture),
("Version", platform.version)] ("Version", platform.version)]
banner = f""" banner = f"""
OCTOSUITE © 2022 Richard Mwewa OCTOSUITE © 2022 Richard Mwewa
{datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')} {datetime.now().strftime('%A %d %B %Y, %H:%M:%S%p')}
""" """
print(banner) print(banner)
print(f"\t{platform.system()}") print(f"\t{platform.system()}")
for key, value in system_info: for key, value in system_info:
print(f"\t├─ {key}: {value()}") print(f"\t├─ {key}: {value()}")
print("\n") print("\n")
while True: while True:
try: try:
color_chooser = input(f"[ ? ] Welcome, would you like to enable colors for this session? (Y/n) ").lower() color_chooser = input(f"[ ? ] Welcome, would you like to enable colors for this session? (Y/n) ").lower()
if color_chooser == "y": if color_chooser == "y":
header_title = "bold white" header_title = "bold white"
red = "[red]" red = "[red]"
white = "[white]" white = "[white]"
green = "[green]" green = "[green]"
red_bold = "[white bold]" red_bold = "[white bold]"
white_bold = "[white bold]" white_bold = "[white bold]"
green_bold = "[green bold]" green_bold = "[green bold]"
reset = "[/]" reset = "[/]"
break break
elif color_chooser == "n": elif color_chooser == "n":
header_title = red = white = green = red_bold = white_bold = green_bold = reset = "" header_title = red = white = green = red_bold = white_bold = green_bold = reset = ""
break break
else: else:
print(f"\n[ ! ] Your response '{color_chooser}' is invalid (expected y or n)") print(f"\n[ ! ] Your response '{color_chooser}' is invalid (expected y or n)")
except KeyboardInterrupt: except KeyboardInterrupt:
exit(f"[ ! ] Process interrupted with [Ctrl+C].") exit(f"[ ! ] Process interrupted with [Ctrl+C].")

View File

@@ -5,10 +5,11 @@ from octosuite.sign_vars import SignVar
from octosuite.log_roller import logRoller from octosuite.log_roller import logRoller
from octosuite.colors import red, white, green, reset from octosuite.colors import red, white, green, reset
''' """
csvLogger csvLogger
This class holds the methods for creating .csv files of each functionality in main This class holds the methods for creating .csv files of each functionality in main
''' """
class csvLogger: class csvLogger:
# .csv for organization' profile # .csv for organization' profile
def logOrgProfile(response): def logOrgProfile(response):

View File

@@ -1,47 +1,60 @@
from rich.table import Table from rich.table import Table
from rich import print as xprint from rich import print as xprint
from octosuite.colors import white, green, white_bold, green_bold, header_title, reset from octosuite.colors import white, green, white_bold, green_bold, header_title, reset
""" """
Help Help
This class holds the help text for available commands. This class holds the help text for available commands.
""" """
class Help: class Help:
usageText = 'Use syntax {} to get started with %s{}%s.' % (green_bold, reset) usageText = 'Use syntax {} to get started with %s{}%s.' % (green_bold, reset)
usageText1 = '%sUse {} to view all available subcommands.%s' % (white, reset) usageText1 = '%sUse {} to view all available subcommands.%s' % (white, reset)
usageText2 = "%sThe {} command works with subcommands. %s" % (white, reset) usageText2 = "%sThe {} command works with subcommands. %s" % (white, reset)
def Org(): def Org():
xprint(Help.usageText2.format(f"{green_bold}org{reset}") + Help.usageText1.format(f"{green_bold}help:org{reset}")) xprint(
Help.usageText2.format(f"{green_bold}org{reset}") + Help.usageText1.format(f"{green_bold}help:org{reset}"))
def Repo(): def Repo():
xprint(Help.usageText2.format(f"{green_bold}repo{reset}") + Help.usageText1.format(f"{green_bold}help:repo{reset}")) xprint(Help.usageText2.format(f"{green_bold}repo{reset}") + Help.usageText1.format(
f"{green_bold}help:repo{reset}"))
def User(): def User():
xprint(Help.usageText2.format(f"{green_bold}user{reset}") + Help.usageText1.format(f"{green_bold}help:user{reset}")) xprint(Help.usageText2.format(f"{green_bold}user{reset}") + Help.usageText1.format(
f"{green_bold}help:user{reset}"))
def Search(): def Search():
xprint(Help.usageText2.format(f"{green_bold}search{reset}") + Help.usageText1.format(f"{green_bold}help:search{reset}")) xprint(Help.usageText2.format(f"{green_bold}search{reset}") + Help.usageText1.format(
f"{green_bold}help:search{reset}"))
def Source(): def Source():
xprint(Help.usageText2.format(f"{green_bold}source{reset}") + Help.usageText1.format(f"{green_bold}help:source{reset}")) xprint(Help.usageText2.format(f"{green_bold}source{reset}") + Help.usageText1.format(
f"{green_bold}help:source{reset}"))
def Logs(): def Logs():
xprint(Help.usageText2.format(f"{green_bold}logs{reset}") + Help.usageText1.format(f"{green_bold}help:logs{reset}")) xprint(Help.usageText2.format(f"{green_bold}logs{reset}") + Help.usageText1.format(
f"{green_bold}help:logs{reset}"))
def Version(): def Version():
xprint(Help.usageText2.format(f"{green_bold}version{reset}") + Help.usageText1.format(f"{green_bold}help:version{reset}")) xprint(Help.usageText2.format(f"{green_bold}version{reset}") + Help.usageText1.format(
f"{green_bold}help:version{reset}"))
def Csv(): def Csv():
xprint(Help.usageText2.format(f"{green_bold}csv{reset}") + Help.usageText1.format(f"{green_bold}help:csv{reset}")) xprint(
Help.usageText2.format(f"{green_bold}csv{reset}") + Help.usageText1.format(f"{green_bold}help:csv{reset}"))
def versionCommand(): def versionCommand():
version_cmd_table =Table(show_header=True, header_style=header_title) version_cmd_table = Table(show_header=True, header_style=header_title)
version_cmd_table.add_column("Command", style="dim", width=12) version_cmd_table.add_column("Command", style="dim", width=12)
version_cmd_table.add_column("Description") version_cmd_table.add_column("Description")
version_cmd_table.add_row("check", "Check for new release(s)") version_cmd_table.add_row("check", "Check for new release(s)")
@@ -53,7 +66,7 @@ class Help:
def sourceCommand(): def sourceCommand():
source_cmd_table =Table(show_header=True, header_style=header_title) source_cmd_table = Table(show_header=True, header_style=header_title)
source_cmd_table.add_column("Command", style="dim", width=12) source_cmd_table.add_column("Command", style="dim", width=12)
source_cmd_table.add_column("Description") source_cmd_table.add_column("Description")
source_cmd_table.add_row("zipball", "Download source code Zipball") source_cmd_table.add_row("zipball", "Download source code Zipball")
@@ -65,7 +78,7 @@ class Help:
def searchCommand(): def searchCommand():
search_cmd_table =Table(show_header=True, header_style=header_title) search_cmd_table = Table(show_header=True, header_style=header_title)
search_cmd_table.add_column("Command", style="dim", width=12) search_cmd_table.add_column("Command", style="dim", width=12)
search_cmd_table.add_column("Description") search_cmd_table.add_column("Description")
search_cmd_table.add_row("users", "Search user(s)") search_cmd_table.add_row("users", "Search user(s)")
@@ -80,7 +93,7 @@ class Help:
def userCommand(): def userCommand():
user_cmd_table =Table(show_header=True, header_style=header_title) user_cmd_table = Table(show_header=True, header_style=header_title)
user_cmd_table.add_column("Command", style="dim", width=12) user_cmd_table.add_column("Command", style="dim", width=12)
user_cmd_table.add_column("Description") user_cmd_table.add_column("Description")
user_cmd_table.add_row("profile", "Get a target's profile info") user_cmd_table.add_row("profile", "Get a target's profile info")
@@ -99,7 +112,7 @@ class Help:
def orgCommand(): def orgCommand():
org_cmd_table =Table(show_header=True, header_style=header_title) org_cmd_table = Table(show_header=True, header_style=header_title)
org_cmd_table.add_column("Command", style="dim", width=12) org_cmd_table.add_column("Command", style="dim", width=12)
org_cmd_table.add_column("Description") org_cmd_table.add_column("Description")
org_cmd_table.add_row("profile", "Get a target organization' profile info") org_cmd_table.add_row("profile", "Get a target organization' profile info")
@@ -113,7 +126,7 @@ class Help:
def repoCommand(): def repoCommand():
repo_cmd_table =Table(show_header=True, header_style=header_title) repo_cmd_table = Table(show_header=True, header_style=header_title)
repo_cmd_table.add_column("Command", style="dim", width=12) repo_cmd_table.add_column("Command", style="dim", width=12)
repo_cmd_table.add_column("Description") repo_cmd_table.add_column("Description")
repo_cmd_table.add_row("profile", "Get a repository's info") repo_cmd_table.add_row("profile", "Get a repository's info")
@@ -129,7 +142,7 @@ class Help:
def logsCommand(): def logsCommand():
logs_cmd_table =Table(show_header=True, header_style=header_title) logs_cmd_table = Table(show_header=True, header_style=header_title)
logs_cmd_table.add_column("Command", style="dim", width=12) logs_cmd_table.add_column("Command", style="dim", width=12)
logs_cmd_table.add_column("Description") logs_cmd_table.add_column("Description")
logs_cmd_table.add_row("view", "View logs") logs_cmd_table.add_row("view", "View logs")
@@ -142,7 +155,7 @@ class Help:
def csvCommand(): def csvCommand():
csv_cmd_table =Table(show_header=True, header_style=header_title) csv_cmd_table = Table(show_header=True, header_style=header_title)
csv_cmd_table.add_column("Command", style="dim", width=12) csv_cmd_table.add_column("Command", style="dim", width=12)
csv_cmd_table.add_column("Description") csv_cmd_table.add_column("Description")
csv_cmd_table.add_row("view", "View csv files") csv_cmd_table.add_row("view", "View csv files")
@@ -155,7 +168,7 @@ class Help:
def helpCommand(): def helpCommand():
core_cmd_table =Table(show_header=True, header_style=header_title) core_cmd_table = Table(show_header=True, header_style=header_title)
core_cmd_table.add_column("Command", style="dim", width=12) core_cmd_table.add_column("Command", style="dim", width=12)
core_cmd_table.add_column("Description") core_cmd_table.add_column("Description")
core_cmd_table.add_row("help", "Help menu") core_cmd_table.add_row("help", "Help menu")
@@ -164,7 +177,7 @@ class Help:
core_cmd_table.add_row("about", "Program's info") core_cmd_table.add_row("about", "Program's info")
core_cmd_table.add_row("author", "Developer's info") core_cmd_table.add_row("author", "Developer's info")
help_sub_cmd_table =Table(show_header=True, header_style=header_title) help_sub_cmd_table = Table(show_header=True, header_style=header_title)
help_sub_cmd_table.add_column("Command", style="dim", width=12) help_sub_cmd_table.add_column("Command", style="dim", width=12)
help_sub_cmd_table.add_column("Description") help_sub_cmd_table.add_column("Description")
help_sub_cmd_table.add_row("csv", "List all csv management commands") help_sub_cmd_table.add_row("csv", "List all csv management commands")

View File

@@ -1,8 +1,9 @@
""" """
logRoller logRoller This class is where the main notification strings/messages are held, and are being used in two different
This class is where the main notification strings/messages are held, cases (they're being used by logging to be written to log files, and being printed out to the screen).
and are being used in two different cases (they're beig used by logging to be written to log files, and being printed out to the screen).
""" """
class logRoller: class logRoller:
Ctrl = "Session terminated with {}." Ctrl = "Session terminated with {}."
Error = "An error occurred: {}" Error = "An error occurred: {}"

View File

@@ -1,9 +1,7 @@
#!usr/bin/python #!usr/bin/python
import os import os
import csv
import sys import sys
import json
import logging import logging
import getpass import getpass
import requests import requests
@@ -17,13 +15,12 @@ from octosuite.sign_vars import SignVar
from octosuite.log_roller import logRoller from octosuite.log_roller import logRoller
from octosuite.csv_loggers import csvLogger from octosuite.csv_loggers import csvLogger
from octosuite.banner import name_logo, version_tag from octosuite.banner import name_logo, version_tag
from octosuite.colors import red, white, green, red_bold, white_bold, green_bold, header_title, reset from octosuite.colors import red, white, green, white_bold, green_bold, header_title, reset
# API endpoint # API endpoint
endpoint = 'https://api.github.com' endpoint = 'https://api.github.com'
# Path attribute # Path attribute
path_attrs =['size','type','path','sha','html_url'] path_attrs = ['size', 'type', 'path', 'sha', 'html_url']
# Path attribute dictionary # Path attribute dictionary
path_attr_dict = {'size': 'Size (bytes)', path_attr_dict = {'size': 'Size (bytes)',
'type': 'Type', 'type': 'Type',
@@ -33,7 +30,9 @@ path_attr_dict = {'size': 'Size (bytes)',
# Organization attributes # Organization attributes
org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at'] org_attrs = ['avatar_url', 'login', 'id', 'node_id', 'email', 'description', 'blog', 'location', 'followers',
'following', 'twitter_username', 'public_gists', 'public_repos', 'type', 'is_verified',
'has_organization_projects', 'has_repository_projects', 'created_at', 'updated_at']
# Organization attribute dictionary # Organization attribute dictionary
org_attr_dict = {'avatar_url': 'Profile Photo', org_attr_dict = {'avatar_url': 'Profile Photo',
'login': 'Username', 'login': 'Username',
@@ -57,7 +56,10 @@ org_attr_dict = {'avatar_url': 'Profile Photo',
# Repository attributes # Repository attributes
repo_attrs = ['id','description','forks','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','fork','allow_forking','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at'] repo_attrs = ['id', 'description', 'forks', 'stargazers_count', 'watchers', 'license', 'default_branch', 'visibility',
'language', 'open_issues', 'topics', 'homepage', 'clone_url', 'ssh_url', 'fork', 'allow_forking',
'private', 'archived', 'has_downloads', 'has_issues', 'has_pages', 'has_projects', 'has_wiki',
'pushed_at', 'created_at', 'updated_at']
# Repository attribute dictionary # Repository attribute dictionary
repo_attr_dict = {'id': 'ID', repo_attr_dict = {'id': 'ID',
'description': 'About', 'description': 'About',
@@ -89,7 +91,8 @@ repo_attr_dict = {'id': 'ID',
# Repo releases attributes # Repo releases attributes
repo_releases_attrs = ['id', 'node_id','tag_name','target_commitish','assets','draft','prerelease','created_at','published_at'] repo_releases_attrs = ['id', 'node_id', 'tag_name', 'target_commitish', 'assets', 'draft', 'prerelease', 'created_at',
'published_at']
# Repo releases attribute dictionary # Repo releases attribute dictionary
repo_releases_attr_dict = {'id': 'ID', repo_releases_attr_dict = {'id': 'ID',
'node_id': 'Node ID', 'node_id': 'Node ID',
@@ -103,7 +106,9 @@ repo_releases_attr_dict = {'id': 'ID',
# Profile attributes # Profile attributes
profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at'] profile_attrs = ['avatar_url', 'login', 'id', 'node_id', 'bio', 'blog', 'location', 'followers', 'following',
'twitter_username', 'public_gists', 'public_repos', 'company', 'hireable', 'site_admin', 'created_at',
'updated_at']
# Profile attribute dictionary # Profile attribute dictionary
profile_attr_dict = {'avatar_url': 'Profile Photo', profile_attr_dict = {'avatar_url': 'Profile Photo',
'login': 'Username', 'login': 'Username',
@@ -125,7 +130,7 @@ profile_attr_dict = {'avatar_url': 'Profile Photo',
# User attributes # User attributes
user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url'] user_attrs = ['avatar_url', 'id', 'node_id', 'gravatar_id', 'site_admin', 'type', 'html_url']
# User attribute dictionary # User attribute dictionary
user_attr_dict = {'avatar_url': 'Profile Photo', user_attr_dict = {'avatar_url': 'Profile Photo',
'id': 'ID', 'id': 'ID',
@@ -137,7 +142,7 @@ user_attr_dict = {'avatar_url': 'Profile Photo',
# Topic atrributes # Topic atrributes
topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at'] topic_attrs = ['score', 'curated', 'featured', 'display_name', 'created_by', 'created_at', 'updated_at']
# Topic attribute dictionary # Topic attribute dictionary
topic_attr_dict = {'score': 'Score', topic_attr_dict = {'score': 'Score',
'curated': 'Curated', 'curated': 'Curated',
@@ -149,7 +154,7 @@ topic_attr_dict = {'score': 'Score',
# Gists attributes # Gists attributes
gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at'] gists_attrs = ['node_id', 'description', 'comments', 'files', 'git_push_url', 'public', 'truncated', 'updated_at']
# Gists attribute dictionary # Gists attribute dictionary
gists_attr_dict = {'node_id': 'Node ID', gists_attr_dict = {'node_id': 'Node ID',
'description': 'About', 'description': 'About',
@@ -162,7 +167,8 @@ gists_attr_dict = {'node_id': 'Node ID',
# Issue attributes # Issue attributes
issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at'] issue_attrs = ['id', 'node_id', 'score', 'state', 'number', 'comments', 'milestone', 'assignee', 'assignees', 'labels',
'locked', 'draft', 'closed_at']
# Issue attribute dict # Issue attribute dict
issue_attr_dict = {'id': 'ID', issue_attr_dict = {'id': 'ID',
'node_id': 'Node ID', 'node_id': 'Node ID',
@@ -181,7 +187,9 @@ issue_attr_dict = {'id': 'ID',
# Repo issues attributes # Repo issues attributes
repo_issues_attrs = ['id','node_id','state', 'reactions','number','comments','milestone','assignee','active_lock_reason', 'author_association','assignees','labels','locked','closed_at','created_at','updated_at'] repo_issues_attrs = ['id', 'node_id', 'state', 'reactions', 'number', 'comments', 'milestone', 'assignee',
'active_lock_reason', 'author_association', 'assignees', 'labels', 'locked', 'closed_at',
'created_at', 'updated_at']
# Issue attribute dict # Issue attribute dict
repo_issues_attr_dict = {'id': 'ID', repo_issues_attr_dict = {'id': 'ID',
'node_id': 'Node ID', 'node_id': 'Node ID',
@@ -202,7 +210,7 @@ repo_issues_attr_dict = {'id': 'ID',
# User organizations attributes # User organizations attributes
user_orgs_attrs = ['avatar_url','id','node_id','url','description'] user_orgs_attrs = ['avatar_url', 'id', 'node_id', 'url', 'description']
user_orgs_attr_dict = {'avatar_url': 'Profile Photo', user_orgs_attr_dict = {'avatar_url': 'Profile Photo',
'id': 'ID', 'id': 'ID',
'node_id': 'Node ID', 'node_id': 'Node ID',
@@ -223,11 +231,15 @@ This function is responsible for creating/checking the availability of the (.lo
enabling logging to automatically log network/user activity to a file, enabling logging to automatically log network/user activity to a file,
and logging the start of a session. and logging the start of a session.
''' '''
def pathFinder(): def pathFinder():
'''
"""
Here we create/check 3 directories (.logs, output, downloads) on startup Here we create/check 3 directories (.logs, output, downloads) on startup
If they exists, we ignore, otherwise, we create them If they exist, we ignore, otherwise, we create them
''' """
directory_list = ['.logs', 'output', 'downloads'] directory_list = ['.logs', 'output', 'downloads']
for directory in directory_list: for directory in directory_list:
os.makedirs(directory, exist_ok=True) os.makedirs(directory, exist_ok=True)
@@ -236,8 +248,9 @@ def pathFinder():
Configure logging to log activities to a file, which will be named by the date and time a session was opened. 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 = datetime.now()
now_formatted = now.strftime("%Y-%m-%d %H:%M:%S%p") 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) 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 # Log the start of a session
logging.info(logRoller.sessionOpened.format(platform.node(), getpass.getuser())) logging.info(logRoller.sessionOpened.format(platform.node(), getpass.getuser()))
@@ -246,22 +259,24 @@ def pathFinder():
onStart() onStart()
This is the main function, responsible for mapping commands, calling other functions, and catching exceptions This is the main function, responsible for mapping commands, calling other functions, and catching exceptions
''' '''
def onStart(): def onStart():
pathFinder() pathFinder()
# A list of tuples mapping commands to their functions # A list of tuples mapping commands to their functions
command_map = [("exit", exitSession), command_map = [("exit", exitSession),
("clear",clearScreen), ("clear", clearScreen),
("about", about), ("about", about),
("author", author), ("author", author),
("help", Help.helpCommand), ("help", Help.helpCommand),
("help:version", Help.versionCommand), ("help:version", Help.versionCommand),
("help:source", Help.sourceCommand), ("help:source", Help.sourceCommand),
("help:search", Help.searchCommand), ("help:search", Help.searchCommand),
("help:user", Help.userCommand), ("help:user", Help.userCommand),
("help:repo", Help.repoCommand), ("help:repo", Help.repoCommand),
("help:logs", Help.logsCommand), ("help:logs", Help.logsCommand),
("help:csv", Help.csvCommand), ("help:csv", Help.csvCommand),
("help:org", Help.orgCommand), ("help:org", Help.orgCommand),
("version", Help.Version), ("version", Help.Version),
("version:info", versionInfo), ("version:info", versionInfo),
("version:check", versionCheck), ("version:check", versionCheck),
@@ -298,9 +313,9 @@ def onStart():
("search:issues", issueSearch), ("search:issues", issueSearch),
("search:commits", commitsSearch), ("search:commits", commitsSearch),
("logs", Help.Logs), ("logs", Help.Logs),
("logs:view",viewLogs), ("logs:view", viewLogs),
("logs:read",readLog), ("logs:read", readLog),
("logs:delete",deleteLog), ("logs:delete", deleteLog),
("csv", Help.Csv), ("csv", Help.Csv),
("csv:view", viewCsv), ("csv:view", viewCsv),
("csv:read", readCsv), ("csv:read", readCsv),
@@ -314,7 +329,10 @@ def onStart():
''' '''
while True: while True:
try: try:
xprint(f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}", end="");command_input = input().lower() xprint(
f"{white}┌──({red}{getpass.getuser()}{white}@{red}octosuite{white})\n├──[~{green}{os.getcwd()}{white}]\n└╼ {reset}",
end="")
command_input = input().lower()
print("\n") print("\n")
''' '''
Iterating over the command_map and check if the user input matches any command in it [command_map], Iterating over the command_map and check if the user input matches any command in it [command_map],
@@ -338,17 +356,19 @@ def onStart():
logging.error(logRoller.Error.format(e)) logging.error(logRoller.Error.format(e))
xprint(f"{SignVar.error} {logRoller.Error.format(e)}") xprint(f"{SignVar.error} {logRoller.Error.format(e)}")
# Fetching organization info
# Fetching organization info
def orgProfile(): def orgProfile():
xprint(f"{white}>> @{green}Organization {white}(username){reset} ", end="");organization = input() xprint(f"{white}>> @{green}Organization {white}(username){reset} ", end="")
organization = input()
response = requests.get(f"{endpoint}/orgs/{organization}") response = requests.get(f"{endpoint}/orgs/{organization}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}")
elif response.status_code == 200: elif response.status_code == 200:
xprint(f"\n{white}{response.json()['name']}{reset}") xprint(f"\n{white}{response.json()['name']}{reset}")
for attr in org_attrs: for attr in org_attrs:
xprint(f"{white}├─ {org_attr_dict[attr]}:{reset} {response.json()[attr]}") xprint(f"{white}├─ {org_attr_dict[attr]}:{reset} {response.json()[attr]}")
csvLogger.logOrgProfile(response) csvLogger.logOrgProfile(response)
else: else:
xprint(response.json()) xprint(response.json())
@@ -356,7 +376,8 @@ def orgProfile():
# Fetching user information # Fetching user information
def userProfile(): def userProfile():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() xprint(f"{white}>> @{green}Username{reset} ", end="")
username = input()
response = requests.get(f"{endpoint}/users/{username}") response = requests.get(f"{endpoint}/users/{username}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
@@ -371,8 +392,10 @@ def userProfile():
# Fetching repository information # Fetching repository information
def repoProfile(): def repoProfile():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() repo_name = input()
xprint(f"{white}>> @{green}Owner{white} (username) ", end="")
username = input()
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
@@ -387,9 +410,12 @@ def repoProfile():
# Get path contents # Get path contents
def pathContents(): def pathContents():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() repo_name = input()
xprint(f"{white}>> ~{green}/path/name{reset} ", end="");path_name = input() xprint(f"{white}>> @{green}Owner{white} (username) ", end="")
username = input()
xprint(f"{white}>> ~{green}/path/name{reset} ", end="")
path_name = input()
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contents/{path_name}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contents/{path_name}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.infoNotFound.format(repo_name, username, path_name)}") xprint(f"{SignVar.negative} {logRoller.infoNotFound.format(repo_name, username, path_name)}")
@@ -399,7 +425,7 @@ def pathContents():
content_count += 1 content_count += 1
xprint(f"\n{white}{content['name']}{reset}") xprint(f"\n{white}{content['name']}{reset}")
for attr in path_attrs: for attr in path_attrs:
xprint(f"{white}├─ {path_attr_dict[attr]}:{reset} {content[attr]}") xprint(f"{white}├─ {path_attr_dict[attr]}:{reset} {content[attr]}")
csvLogger.logRepoPathContents(content, repo_name) csvLogger.logRepoPathContents(content, repo_name)
xprint(SignVar.info, f"Found {content_count} file(s) in {repo_name}/{path_name}.") xprint(SignVar.info, f"Found {content_count} file(s) in {repo_name}/{path_name}.")
else: else:
@@ -408,9 +434,12 @@ def pathContents():
# repo contributors # repo contributors
def repoContributors(): def repoContributors():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username) ", end="");username = input() repo_name = input()
xprint(SignVar.prompt, logRoller.limitInput.format("contributors"), end="");limit = int(input()) xprint(f"{white}>> @{green}Owner{white} (username) ", end="")
username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("contributors"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
@@ -426,9 +455,12 @@ def repoContributors():
# repo stargazers # repo stargazers
def repoStargazers(): def repoStargazers():
xprint(f"{white}>> %{green}Repository{reset} ");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() repo_name = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository stargazers"), end="");limit = int(input()) xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="")
username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository stargazers"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
@@ -437,7 +469,7 @@ def repoStargazers():
elif response.status_code == 200: elif response.status_code == 200:
for stargazer in response.json(): for stargazer in response.json():
xprint(f"\n{white}{stargazer['login']}{reset}") xprint(f"\n{white}{stargazer['login']}{reset}")
for attr in user_attrs: for attr in user_attrs:
xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {stargazer[attr]}") xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {stargazer[attr]}")
csvLogger.logRepoStargazers(stargazer, repo_name) csvLogger.logRepoStargazers(stargazer, repo_name)
else: else:
@@ -446,9 +478,12 @@ def repoStargazers():
# repo forks # repo forks
def repoForks(): def repoForks():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() repo_name = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository forks"), end="");limit = int(input()) xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="")
username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository forks"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
@@ -459,7 +494,7 @@ def repoForks():
for fork in response.json(): for fork in response.json():
count += 1 count += 1
xprint(f"\n{white}{fork['full_name']}{reset}") xprint(f"\n{white}{fork['full_name']}{reset}")
for attr in repo_attrs: for attr in repo_attrs:
xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {fork[attr]}") xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {fork[attr]}")
csvLogger.logRepoForks(fork, count) csvLogger.logRepoForks(fork, count)
else: else:
@@ -468,75 +503,85 @@ def repoForks():
# Repo issues # Repo issues
def repoIssues(): def repoIssues():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() repo_name = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository issues"), end="");limit = int(input()) xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="")
username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository issues"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
elif response.json() == []: elif response.json() == []:
xprint(f"{SignVar.negative} Repository does not have open issues -> ({repo_name})") xprint(f"{SignVar.negative} Repository does not have open issues -> ({repo_name})")
elif response.status_code == 200: elif response.status_code == 200:
for issue in response.json(): for issue in response.json():
xprint(f"\n{white}{issue['title']}{reset}") xprint(f"\n{white}{issue['title']}{reset}")
for attr in repo_issues_attrs: for attr in repo_issues_attrs:
xprint(f"{white}├─ {repo_issues_attr_dict[attr]}:{reset} {issue[attr]}") xprint(f"{white}├─ {repo_issues_attr_dict[attr]}:{reset} {issue[attr]}")
xprint(issue['body']) xprint(issue['body'])
csvLogger.logRepoIssues(issue, repo_name) csvLogger.logRepoIssues(issue, repo_name)
else: else:
xprint(response.json()) xprint(response.json())
# Repo releases # Repo releases
def repoReleases(): def repoReleases():
xprint(f"{white}>> %{green}Repository{reset} ", end="");repo_name = input() xprint(f"{white}>> %{green}Repository{reset} ", end="")
xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="");username = input() repo_name = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository releases"), end="");limit = int(input()) xprint(f"{white}>> @{green}Owner{white} (username){reset} ", end="")
username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repository releases"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}") response = requests.get(f"{endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}") xprint(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}")
elif response.json() == []: elif response.json() == []:
xprint(f"{SignVar.negative} Repository does not have releases -> ({repo_name})") xprint(f"{SignVar.negative} Repository does not have releases -> ({repo_name})")
elif response.status_code == 200: elif response.status_code == 200:
for release in response.json(): for release in response.json():
xprint(f"\n{white}{release['name']}{reset}") xprint(f"\n{white}{release['name']}{reset}")
for attr in repo_releases_attrs: for attr in repo_releases_attrs:
xprint(f"{white}├─ {repo_releases_attr_dict[attr]}:{reset} {release[attr]}") xprint(f"{white}├─ {repo_releases_attr_dict[attr]}:{reset} {release[attr]}")
xprint(release['body']) xprint(release['body'])
csvLogger.logRepoReleases(release, repo_name) csvLogger.logRepoReleases(release, repo_name)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching organization repositories # Fetching organization repositories
def orgRepos(): def orgRepos():
xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="");limit = int(input()) organization = input()
xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/orgs/{organization}/repos?per_page={limit}") response = requests.get(f"{endpoint}/orgs/{organization}/repos?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}")
elif response.status_code == 200: elif response.status_code == 200:
for repository in response.json(): for repository in response.json():
xprint(f"\n{white}{repository['full_name']}{reset}") xprint(f"\n{white}{repository['full_name']}{reset}")
for attr in repo_attrs: for attr in repo_attrs:
xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}") xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}")
csvLogger.logOrgRepos(repository, organization) csvLogger.logOrgRepos(repository, organization)
else: else:
xprint(response.json()) xprint(response.json())
# organization events # organization events
def orgEvents(): def orgEvents():
xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="");limit = int(input()) organization = input()
xprint(SignVar.prompt, logRoller.limitInput.format("organization repositories"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/orgs/{organization}/events?per_page={limit}") response = requests.get(f"{endpoint}/orgs/{organization}/events?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}") xprint(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}")
elif response.status_code == 200: elif response.status_code == 200:
for event in response.json(): for event in response.json():
xprint(f"\n{white}{event['id']}{reset}") xprint(f"\n{white}{event['id']}{reset}")
xprint(f"{white}├─ Type:{reset} {event['type']}\n{white}├─ Created at:{reset} {event['created_at']}") xprint(f"{white}├─ Type:{reset} {event['type']}\n{white}├─ Created at:{reset} {event['created_at']}")
xprint(event['payload']) xprint(event['payload'])
csvLogger.logOrgEvents(event, organization) csvLogger.logOrgEvents(event, organization)
else: else:
xprint(response.json()) xprint(response.json())
@@ -544,48 +589,54 @@ def orgEvents():
# organization member # organization member
def orgMember(): def orgMember():
xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="");organization = input() xprint(f"{white}>> @{green}Organization{white} (username){reset} ", end="")
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() organization = input()
xprint(f"{white}>> @{green}Username{reset} ", end="")
username = input()
response = requests.get(f"{endpoint}/orgs/{organization}/public_members/{username}") response = requests.get(f"{endpoint}/orgs/{organization}/public_members/{username}")
if response.status_code == 204: if response.status_code == 204:
xprint(f"{SignVar.positive} User ({username}) is a public member of the organization -> ({organization})") xprint(f"{SignVar.positive} User ({username}) is a public member of the organization -> ({organization})")
else: else:
xprint(f"{SignVar.negative} {response.json()['message']}") xprint(f"{SignVar.negative} {response.json()['message']}")
# Fetching user repositories # Fetching user repositories
def userRepos(): def userRepos():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("repositories"), end="");limit = int(input()) username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repositories"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/repos?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/repos?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for repository in response.json(): for repository in response.json():
xprint(f"\n{white}{repository['full_name']}{reset}") xprint(f"\n{white}{repository['full_name']}{reset}")
for attr in repo_attrs: for attr in repo_attrs:
xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}") xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}")
csvLogger.logUserRepos(repository, username) csvLogger.logUserRepos(repository, username)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching user's gists # Fetching user's gists
def userGists(): def userGists():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format('gists'), end="");limit = int(input()) username = input()
xprint(SignVar.prompt, logRoller.limitInput.format('gists'), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/gists?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/gists?per_page={limit}")
#xprint(response.json()) # xprint(response.json())
if response.json() == []: if response.json() == []:
xprint(f"{SignVar.negative} User does not have gists.") xprint(f"{SignVar.negative} User does not have gists.")
elif response.status_code == 404: elif response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for gist in response.json(): for gist in response.json():
xprint(f"\n{white}{gist['id']}{reset}") xprint(f"\n{white}{gist['id']}{reset}")
for attr in gists_attrs: for attr in gists_attrs:
xprint(f"{white}├─ {gists_attr_dict[attr]}:{reset} {gist[attr]}") xprint(f"{white}├─ {gists_attr_dict[attr]}:{reset} {gist[attr]}")
csvLogger.logUserGists(gist) csvLogger.logUserGists(gist)
else: else:
xprint(response.json()) xprint(response.json())
@@ -593,127 +644,144 @@ def userGists():
# Fetching a list of organizations that a user owns or belongs to # Fetching a list of organizations that a user owns or belongs to
def userOrgs(): def userOrgs():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("user organizations"), end="");limit = int(input()) username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("user organizations"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/orgs?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/orgs?per_page={limit}")
if response.json() == []: if response.json() == []:
xprint(f"{SignVar.negative} User ({username}) does not (belong to/own) any organizations.") xprint(f"{SignVar.negative} User ({username}) does not (belong to/own) any organizations.")
elif response.status_code == 404: elif response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for organization in response.json(): for organization in response.json():
print(f"\n{white}{organization['login']}{reset}") print(f"\n{white}{organization['login']}{reset}")
for attr in user_orgs_attrs: for attr in user_orgs_attrs:
xprint(f"{white}├─ {user_orgs_attr_dict[attr]}:{reset} {organization[attr]}") xprint(f"{white}├─ {user_orgs_attr_dict[attr]}:{reset} {organization[attr]}")
csvLogger.logUserOrgs(organization, username) csvLogger.logUserOrgs(organization, username)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching a users events # Fetching a users events
def userEvents(): def userEvents():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("events"), end="");limit = int(input()) username = input()
xprint(SignVar.prompt, logRoller.limitInput.format("events"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/events/public?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/events/public?per_page={limit}")
if response.status_code == 404: if response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for event in response.json(): for event in response.json():
xprint(f"\n{white}{event['id']}{reset}") xprint(f"\n{white}{event['id']}{reset}")
xprint(f"{white}├─ Actor:{reset} {event['actor']['login']}") xprint(f"{white}├─ Actor:{reset} {event['actor']['login']}")
xprint(f"{white}├─ Type:{reset} {event['type']}") xprint(f"{white}├─ Type:{reset} {event['type']}")
xprint(f"{white}├─ Repository:{reset} {event['repo']['name']}") xprint(f"{white}├─ Repository:{reset} {event['repo']['name']}")
xprint(f"{white}├─ Created at:{reset} {event['created_at']}") xprint(f"{white}├─ Created at:{reset} {event['created_at']}")
xprint(event['payload']) xprint(event['payload'])
csvLogger.logUserEvents(event) csvLogger.logUserEvents(event)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching a target user's subscriptions # Fetching a target user's subscriptions
def userSubscriptions(): def userSubscriptions():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("user subscriptions"), end="");limit = int(input()) username = input().lower()
xprint(SignVar.prompt, logRoller.limitInput.format("user subscriptions"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/subscriptions?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/subscriptions?per_page={limit}")
if response.json() == []: if response.json() == []:
xprint(f"{SignVar.negative} User does not have any subscriptions.") xprint(f"{SignVar.negative} User does not have any subscriptions.")
elif response.status_code == 404: elif response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for repository in response.json(): for repository in response.json():
xprint(f"\n{white}{repository['full_name']}{reset}") xprint(f"\n{white}{repository['full_name']}{reset}")
for attr in repo_attrs: for attr in repo_attrs:
xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}") xprint(f"{white}├─ {repo_attr_dict[attr]}:{reset} {repository[attr]}")
csvLogger.logUserSubscriptions(repository, username) csvLogger.logUserSubscriptions(repository, username)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching a list of users the target follows # Fetching a list of users the target follows
def userFollowing(): def userFollowing():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("user' following"), end="");limit = int(input()) username = input().lower()
xprint(SignVar.prompt, logRoller.limitInput.format("user' following"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/following?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/following?per_page={limit}")
if response.json() == []: if response.json() == []:
xprint(f"{SignVar.negative} User ({username})does not follow anyone.") xprint(f"{SignVar.negative} User ({username})does not follow anyone.")
elif response.status_code == 404: elif response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for user in response.json(): for user in response.json():
xprint(f"\n{white}@{user['login']}{reset}") xprint(f"\n{white}@{user['login']}{reset}")
for attr in user_attrs: for attr in user_attrs:
xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {user[attr]}") xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {user[attr]}")
csvLogger.logUserFollowing(user, username) csvLogger.logUserFollowing(user, username)
else: else:
xprint(response.json()) xprint(response.json())
# Fetching user's followera' # Fetching user's followers
def userFollowers(): def userFollowers():
xprint(f"{white}>> @{green}Username{reset} ", end="");username = input().lower() xprint(f"{white}>> @{green}Username{reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("user followers"),end="");limit = int(input()) username = input().lower()
xprint(SignVar.prompt, logRoller.limitInput.format("user followers"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/users/{username}/followers?per_page={limit}") response = requests.get(f"{endpoint}/users/{username}/followers?per_page={limit}")
if response.json() == []: if response.json() == []:
xprint(f"{SignVar.negative} User ({username})does not have followers.") xprint(f"{SignVar.negative} User ({username})does not have followers.")
elif response.status_code == 404: elif response.status_code == 404:
xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}") xprint(f"{SignVar.negative} {logRoller.userNotFound.format(username)}")
elif response.status_code == 200: elif response.status_code == 200:
for follower in response.json(): for follower in response.json():
xprint(f"\n{white}@{follower['login']}{reset}") xprint(f"\n{white}@{follower['login']}{reset}")
for attr in user_attrs: for attr in user_attrs:
xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {follower[attr]}") xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {follower[attr]}")
csvLogger.logUserFollowers(follower, username) csvLogger.logUserFollowers(follower, username)
else: else:
xprint(response.json()) xprint(response.json())
# Checking whether or not user[A] follows user[B] # Checking whether user[A] follows user[B]
def userFollows(): def userFollows():
xprint(f"{white}>> @{green}user{white}(A) (username){reset} ", end="");user_a = input() xprint(f"{white}>> @{green}user{white}(A) (username){reset} ", end="")
xprint(f"{white}>> @{green}user{white}(B) (username){reset} ", end="");user_b = input() user_a = input()
xprint(f"{white}>> @{green}user{white}(B) (username){reset} ", end="")
user_b = input()
response = requests.get(f"{endpoint}/users/{user_a}/following/{user_b}") response = requests.get(f"{endpoint}/users/{user_a}/following/{user_b}")
if response.status_code == 204: if response.status_code == 204:
xprint(f"{SignVar.positive} @{user_a} FOLLOWS @{user_b}") xprint(f"{SignVar.positive} @{user_a} FOLLOWS @{user_b}")
else: else:
xprint(f"{SignVar.negative} @{user_a} DOES NOT FOLLOW @{user_b}") xprint(f"{SignVar.negative} @{user_a} DOES NOT FOLLOW @{user_b}")
# User search
# User search
def userSearch(): def userSearch():
xprint(f"{white}>> @{green}Query{white} (eg. john){reset} ", end="");query = input() xprint(f"{white}>> @{green}Query{white} (eg. john){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("user search"), end="");limit = int(input()) query = input()
xprint(SignVar.prompt, logRoller.limitInput.format("user search"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/search/users?q={query}&per_page={limit}").json() response = requests.get(f"{endpoint}/search/users?q={query}&per_page={limit}").json()
for user in response['items']: for user in response['items']:
xprint(f"\n{white}@{user['login']}{reset}") xprint(f"\n{white}@{user['login']}{reset}")
for attr in user_attrs: for attr in user_attrs:
xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {user[attr]}") xprint(f"{white}├─ {user_attr_dict[attr]}:{reset} {user[attr]}")
csvLogger.logUserSearch(user, query) csvLogger.logUserSearch(user, query)
# Repository search # Repository search
def repoSearch(): def repoSearch():
xprint(f"{white}>> %{green}Query{white} (eg. git){reset} ", end="");query = input() xprint(f"{white}>> %{green}Query{white} (eg. git){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("repositor[y][ies] search"), end="");limit = int(input()) query = input()
xprint(SignVar.prompt, logRoller.limitInput.format("repositor[y][ies] search"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/search/repositories?q={query}&per_page={limit}").json() response = requests.get(f"{endpoint}/search/repositories?q={query}&per_page={limit}").json()
for repository in response['items']: for repository in response['items']:
xprint(f"\n{white}{repository['full_name']}{reset}") xprint(f"\n{white}{repository['full_name']}{reset}")
@@ -724,8 +792,10 @@ def repoSearch():
# Topics search # Topics search
def topicSearch(): def topicSearch():
xprint(f"{white}>> #{green}Query{white} (eg. osint){reset} ", end="");query = input() xprint(f"{white}>> #{green}Query{white} (eg. osint){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("topic(s) search"),end="");limit = int(input()) query = input()
xprint(SignVar.prompt, logRoller.limitInput.format("topic(s) search"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/search/topics?q={query}&per_page={limit}").json() response = requests.get(f"{endpoint}/search/topics?q={query}&per_page={limit}").json()
for topic in response['items']: for topic in response['items']:
xprint(f"\n{white}{topic['name']}{reset}") xprint(f"\n{white}{topic['name']}{reset}")
@@ -736,8 +806,10 @@ def topicSearch():
# Issue search # Issue search
def issueSearch(): def issueSearch():
xprint(f"{white}>> !{green}Query{white} (eg. error){reset} ", end="");query = input() xprint(f"{white}>> !{green}Query{white} (eg. error){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("issue(s) search"), end="");limit = int(input()) query = input()
xprint(SignVar.prompt, logRoller.limitInput.format("issue(s) search"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/search/issues?q={query}&per_page={limit}").json() response = requests.get(f"{endpoint}/search/issues?q={query}&per_page={limit}").json()
for issue in response['items']: for issue in response['items']:
xprint(f"\n\n{white}{issue['title']}{reset}") xprint(f"\n\n{white}{issue['title']}{reset}")
@@ -749,49 +821,49 @@ def issueSearch():
# Commits search # Commits search
def commitsSearch(): def commitsSearch():
xprint(f"{white}>> :{green}Query{white} (eg. filename:index.php){reset} ", end="");query = input() xprint(f"{white}>> :{green}Query{white} (eg. filename:index.php){reset} ", end="")
xprint(SignVar.prompt, logRoller.limitInput.format("commit(s) search"), end="");limit = int(input()) query = input()
xprint(SignVar.prompt, logRoller.limitInput.format("commit(s) search"), end="")
limit = int(input())
response = requests.get(f"{endpoint}/search/commits?q={query}&per_page={limit}").json() response = requests.get(f"{endpoint}/search/commits?q={query}&per_page={limit}").json()
for commit in response['items']: for commit in response['items']:
xprint(f"\n{white}{commit['commit']['tree']['sha']}{reset}") xprint(f"\n{white}{commit['commit']['tree']['sha']}{reset}")
xprint(f"{white}├─ Author:{reset} {commit['commit']['author']['name']}") xprint(f"{white}├─ Author:{reset} {commit['commit']['author']['name']}")
xprint(f"{white}├─ Username:{reset} {commit['author']['login']}") xprint(f"{white}├─ Username:{reset} {commit['author']['login']}")
xprint(f"{white}├─ Email:{reset} {commit['commit']['author']['email']}") xprint(f"{white}├─ Email:{reset} {commit['commit']['author']['email']}")
xprint(f"{white}├─ Commiter:{reset} {commit['commit']['committer']['name']}") xprint(f"{white}├─ Commiter:{reset} {commit['commit']['committer']['name']}")
xprint(f"{white}├─ Repository:{reset} {commit['repository']['full_name']}") xprint(f"{white}├─ Repository:{reset} {commit['repository']['full_name']}")
xprint(f"{white}├─ URL:{reset} {commit['html_url']}") xprint(f"{white}├─ URL:{reset} {commit['html_url']}")
xprint(commit['commit']['message']) xprint(commit['commit']['message'])
csvLogger.logCommitsSearch(commit, query) csvLogger.logCommitsSearch(commit, query)
# View csv files # View csv files
def viewCsv(): def viewCsv():
logging.info(logRoller.viewingCsv) logging.info(logRoller.viewingCsv)
csv_files = os.listdir("output") csv_files = os.listdir("output")
csv_table =Table(show_header=True, header_style=header_title) csv_table = Table(show_header=True, header_style=header_title)
csv_table.add_column("CSV", style="dim", width=12) csv_table.add_column("CSV", style="dim", width=12)
csv_table.add_column("Size (bytes)") csv_table.add_column("Size (bytes)")
for csv_file in csv_files: for csv_file in csv_files:
csv_table.add_row(str(csv_file), str(os.path.getsize("output/"+csv_file))) csv_table.add_row(str(csv_file), str(os.path.getsize("output/" + csv_file)))
xprint(csv_table) xprint(csv_table)
# Read a specified csv file # Read a specified csv file
def readCsv(): def readCsv():
xprint(f"{white}>> {green}.csv {reset}(filename) ", end="");csv_file = input() xprint(f"{white}>> {green}.csv {reset}(filename) ", end="")
csv_file = input()
with open(f"output/{csv_file}.csv", "r") as file: with open(f"output/{csv_file}.csv", "r") as file:
logging.info(logRoller.readingCsv.format(csv_file)) logging.info(logRoller.readingCsv.format(csv_file))
xprint("\n"+file.read()) xprint("\n" + file.read())
# Delete a specified csv file # Delete a specified csv file
def deleteCsv(): def deleteCsv():
xprint(f"{white}>> {green}.csv {reset}filename{reset} ", end="");csv_file = input() xprint(f"{white}>> {green}.csv {reset}filename{reset} ", end="")
if sys.platform.lower().startswith(("win", "darwin")): csv_file = input()
subprocess.run(['del',f'.output\{csv_file}.csv']) os.remove(f'output/{csv_file}')
else:
subprocess.run(['sudo','rm',f'output/{csv_file}.csv'],shell=False)
logging.info(logRoller.deletedCsv.format(csv_file)) logging.info(logRoller.deletedCsv.format(csv_file))
xprint(f"{SignVar.positive} {logRoller.deletedCsv.format(csv_file)}") xprint(f"{SignVar.positive} {logRoller.deletedCsv.format(csv_file)}")
@@ -800,30 +872,28 @@ def deleteCsv():
def viewLogs(): def viewLogs():
logging.info(logRoller.viewingLogs) logging.info(logRoller.viewingLogs)
logs = os.listdir(".logs") logs = os.listdir(".logs")
logs_table =Table(show_header=True, header_style=header_title) logs_table = Table(show_header=True, header_style=header_title)
logs_table.add_column("Log", style="dim", width=12) logs_table.add_column("Log", style="dim", width=12)
logs_table.add_column("Size (bytes)") logs_table.add_column("Size (bytes)")
for log in logs: for log in logs:
logs_table.add_row(str(log), str(os.path.getsize(".logs/"+log))) logs_table.add_row(str(log), str(os.path.getsize(".logs/" + log)))
xprint(logs_table) xprint(logs_table)
# Read a specified log file # Read a specified log file
def readLog(): def readLog():
xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="");log_file = input() xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="")
log_file = input()
with open(f".logs/{log_file}.log", "r") as log: with open(f".logs/{log_file}.log", "r") as log:
logging.info(logRoller.readingLog.format(log_file)) logging.info(logRoller.readingLog.format(log_file))
xprint("\n"+log.read()) xprint("\n" + log.read())
# Delete a specified log file # Delete a specified log file
def deleteLog(): def deleteLog():
xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="");log_file = input() xprint(f"{white}>> {green}.log date{reset} (eg. 2022-04-27 10:09:36AM) ", end="")
if sys.platform.lower().startswith(("win", "darwin")): log_file = input()
subprocess.run(['del',f'.logs\{log_file}.log']) os.remove(f'.logs/{log_file}')
else:
subprocess.run(['sudo','rm',f'.logs/{log_file}.log'],shell=False)
logging.info(logRoller.deletedLog.format(log_file)) logging.info(logRoller.deletedLog.format(log_file))
xprint(f"{SignVar.positive} {logRoller.deletedLog.format(log_file)}") xprint(f"{SignVar.positive} {logRoller.deletedLog.format(log_file)}")
@@ -859,14 +929,15 @@ def versionCheck():
if response.json()['tag_name'] == version_tag: if response.json()['tag_name'] == version_tag:
xprint(f"{SignVar.positive} Octosuite is up to date. Check again soon! :)") xprint(f"{SignVar.positive} Octosuite is up to date. Check again soon! :)")
else: else:
xprint(f"{SignVar.info} A new release is available (octosuite.v{response.json()['tag_name']}). Exit Octosuite and run '{green_bold}pip install --upgrade octosuite{white}' to download and install the update.") xprint(
f"{SignVar.info} A new release is available (octosuite.v{response.json()['tag_name']}). Exit Octosuite and run '{green_bold}pip install --upgrade octosuite{white}' to download and install the update.")
# Author info # Author info
def author(): def author():
xprint(f"{white}Richard Mwewa (Ritchie){reset}") xprint(f"{white}Richard Mwewa (Ritchie){reset}")
for key,value in author_dict.items(): for key, value in author_dict.items():
xprint(f"{white}├─ {key}:{reset} {value}") xprint(f"{white}├─ {key}:{reset} {value}")
# About program # About program
@@ -886,7 +957,8 @@ With over 20+ features, Octosuite only runs on 2 external dependencies, and retu
# Close session # Close session
def exitSession(): def exitSession():
xprint(f"{SignVar.prompt} This will close the current session, continue? (Y/n) ", end="");prompt = input().lower() xprint(f"{SignVar.prompt} This will close the current session, continue? (Y/n) ", end="")
prompt = input().lower()
if prompt == 'y': if prompt == 'y':
logging.info(logRoller.sessionClosed.format(datetime.now())) logging.info(logRoller.sessionClosed.format(datetime.now()))
xprint(f"{SignVar.info} {logRoller.sessionClosed.format(datetime.now())}") xprint(f"{SignVar.info} {logRoller.sessionClosed.format(datetime.now())}")
@@ -897,23 +969,21 @@ def exitSession():
# Clear screen # Clear screen
def clearScreen(): def clearScreen():
''' """
We use 'cls' on Windows machines to clear the screen, We use 'cls' on Windows machines to clear the screen,
otherwise, we use 'clear' otherwise, we use 'clear'
''' """
if sys.platform.lower().startswith(("win", "darwin")): if sys.platform.lower().startswith("win"):
subprocess.run(['cls']) os.system('cls')
else: else:
subprocess.run(['clear'], shell=False) subprocess.run(['clear'], shell=False)
# Show version information # Show version information
def versionInfo(): def versionInfo():
''' # Yes... the changelog is hard coded lol
Yes... the changelog is hard coded lol xprint(f"""
'''
xprint(f"""
{white_bold}Whats new in v{version_tag}?{reset} {white_bold}Whats new in v{version_tag}?{reset}
[ {green}improvement{reset} ] The Octosuite GUI (.exe/.app) is now available on GitHub [ {green}fixed{reset} ] [ Errno 22 ] Invalid argument (on Windows machines)
[ {green}fixed{reset} ] [ Error 2 ] The system cannot find the file specified (on Windows machines)
""") """)

View File

@@ -1,15 +1,14 @@
from octosuite.colors import red, white, green, reset from octosuite.colors import red, white, green, reset
''' """
SignVar SignVar *Even here, I couldn't think of a good name.* The Attributes class holds the signs/symbols that show what
*Even here, I couldn't think of a good name.* a notification in OctoSuite might be all about. This might not be very important or necessary in some cases,
The Attributes class holds the signs/symbols that show what a notification in OctoSuite might be all about. but I think it's better to know the severity of the notifications you get in a program.
This might not be very important or necessary in some cases, but I think it's better to know the severerity of the notifications you get in a program. """
'''
class SignVar: class SignVar:
prompt = f"{white}[{green} ? {white}]{reset}" prompt = f"{white}[{green} ? {white}]{reset}"
warning = f"{white}[{red} ! {white}]{reset}" warning = f"{white}[{red} ! {white}]{reset}"
error = f"{white}[{red} x {white}]{reset}" error = f"{white}[{red} x {white}]{reset}"
positive = f"{white}[{green} + {white}]{reset}" positive = f"{white}[{green} + {white}]{reset}"
negative = f"{white}[{red} - {white}]{reset}" negative = f"{white}[{red} - {white}]{reset}"
info = f"{white}[{green} * {white}]{reset}" info = f"{white}[{green} * {white}]{reset}"

View File

@@ -5,9 +5,9 @@ with open("README.md", "r", encoding="utf-8") as file:
setuptools.setup( setuptools.setup(
name="octosuite", name="octosuite",
version="2.2.2", version="2.2.3",
author="Richard Mwewa", author="Richard Mwewa",
author_email="richardmwewa@duck.com", author_email="rly0nheart@duck.com",
packages=["octosuite"], packages=["octosuite"],
description="Advanced Github OSINT Framework", description="Advanced Github OSINT Framework",
long_description=long_description, long_description=long_description,