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,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,43 +1,56 @@
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():

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,8 +15,7 @@ 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'
@@ -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',
@@ -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',
@@ -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,6 +259,8 @@ 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
@@ -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,10 +356,12 @@ 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)}")
@@ -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)}")
@@ -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)}")
@@ -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)}")
@@ -468,9 +503,12 @@ 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)}")
@@ -489,9 +527,12 @@ def repoIssues():
# 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)}")
@@ -510,8 +551,10 @@ def repoReleases():
# 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)}")
@@ -527,8 +570,10 @@ def orgRepos():
# 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)}")
@@ -544,8 +589,10 @@ 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})")
@@ -555,8 +602,10 @@ def orgMember():
# 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)}")
@@ -572,8 +621,10 @@ def userRepos():
# 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() == []:
@@ -593,8 +644,10 @@ 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.")
@@ -612,8 +665,10 @@ def userOrgs():
# 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)}")
@@ -632,8 +687,10 @@ def userEvents():
# 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.")
@@ -651,8 +708,10 @@ def userSubscriptions():
# 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.")
@@ -668,10 +727,12 @@ def userFollowing():
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.")
@@ -687,21 +748,26 @@ def userFollowers():
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}")
@@ -712,8 +778,10 @@ def userSearch():
# 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,8 +821,10 @@ 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}")
@@ -778,7 +852,8 @@ def viewCsv():
# 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())
@@ -786,12 +861,9 @@ def readCsv():
# 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)}")
@@ -810,7 +882,8 @@ def viewLogs():
# 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())
@@ -818,12 +891,9 @@ def readLog():
# 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,7 +929,8 @@ 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
@@ -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,11 +1,10 @@
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}"

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,