mirror of
https://github.com/bellingcat/octosuite.git
synced 2026-06-10 20:38:34 +03:00
Compare commits
51 Commits
2.0.1-alph
...
2.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11e55f7804 | ||
|
|
96c62c7097 | ||
|
|
587641cd18 | ||
|
|
d1ad3d14b9 | ||
|
|
0e3568f967 | ||
|
|
51d27c4996 | ||
|
|
ae9d9f4af5 | ||
|
|
482f2ee55a | ||
|
|
9894b15209 | ||
|
|
4d563683d2 | ||
|
|
614a24d69c | ||
|
|
2c18f116e9 | ||
|
|
f67e876773 | ||
|
|
cf7ab5eef1 | ||
|
|
bdb88135fe | ||
|
|
e062065c75 | ||
|
|
b2557fd714 | ||
|
|
2a025190bd | ||
|
|
3b4f38e76a | ||
|
|
5bffb8a4b5 | ||
|
|
1e95f70e3e | ||
|
|
88cc04a104 | ||
|
|
3339614984 | ||
|
|
c9cb27f3fd | ||
|
|
f58736f27d | ||
|
|
822be0d088 | ||
|
|
9fe13482ff | ||
|
|
c4b8e70762 | ||
|
|
557e64c22f | ||
|
|
ba6d6da734 | ||
|
|
94a22c6de1 | ||
|
|
0ff5765dc5 | ||
|
|
bb4596c294 | ||
|
|
8967e9ae7a | ||
|
|
ccb40ec942 | ||
|
|
4e2291eba3 | ||
|
|
a9787efbe1 | ||
|
|
efd7b44131 | ||
|
|
2f40b22ab5 | ||
|
|
17075b8a4b | ||
|
|
5d7f8cfbd7 | ||
|
|
daffefe24e | ||
|
|
887957b73c | ||
|
|
2ab70c0737 | ||
|
|
0bca1d6e42 | ||
|
|
8f426eae26 | ||
|
|
d350a37cb6 | ||
|
|
151e8ba3dd | ||
|
|
23f3b43362 | ||
|
|
985325550b | ||
|
|
be3e14889d |
39
.github/workflows/python-publish.yml
vendored
Normal file
39
.github/workflows/python-publish.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install build
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
48
README.md
48
README.md
@@ -1,22 +1,17 @@
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
[](https://github.com/rly0nheart/octosuite/actions/workflows/python-publish.yml)
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> *Simply gather OSINT on Github users & organizations like a God🔥*
|
||||
|
||||
# INSTALLATION
|
||||
Installation instructions are on the wiki, in addition to all other documentation.
|
||||
## Installation
|
||||
[Refer to the Wiki](https://github.com/rly0nheart/octosuite/wiki) for installation instructions, in addition to all other documentation.
|
||||
|
||||
[Refer to the Wiki](https://github.com/rly0nheart/octosuite/wiki)
|
||||
|
||||
|
||||
# FEATURES
|
||||
## Features
|
||||
- [x] Fetches an organization's profile information
|
||||
- [x] Fetches an oganization's events
|
||||
- [x] Returns an organization's repositories
|
||||
@@ -32,6 +27,7 @@ Installation instructions are on the wiki, in addition to all other documentatio
|
||||
- [x] Returns a user's gists
|
||||
- [x] Returns organizations that a user owns/belongs to
|
||||
- [x] Fetches a user's events
|
||||
- [x] Fetches a list of users followed by the target
|
||||
- [x] Fetches a user's followers
|
||||
- [x] Checks if user A follows user B
|
||||
- [x] Checks if user is a public member of an organizations
|
||||
@@ -43,36 +39,28 @@ Installation instructions are on the wiki, in addition to all other documentatio
|
||||
- [x] Searches topics
|
||||
- [x] Searches issues
|
||||
- [x] Searches commits
|
||||
- [x] Easily updates with the 'update:install' command
|
||||
- [x] Automatically logs network activity (.logs folder)
|
||||
- [x] User can view, read and delete logs
|
||||
- [x] ...And more
|
||||
|
||||
# NOTES
|
||||
* *octosuite automatically logs network and minor user activity. The logs are saved by date and time in .logs folder*
|
||||
* *Although octosuite was developed to work on **Mac**, **Windows**, or any **Linux** *Distribution*, it has only been tested on **Termux** *and* **Kali Linux***
|
||||
* *If you believe octosuite can be better, feel free to open a pull request with your improvements* ✌🏾🙂
|
||||
> *octosuite automatically logs network and minor user activity of each session. The logs are saved by date and time in the .logs folder*
|
||||
>> *Although octosuite was developed to work on **Mac**, **Windows**, or any **Linux** *Distribution*, it has only been tested on **Termux** *and* **Kali Linux***
|
||||
>>> *If you believe octosuite can be better, feel free to open a pull request with your improvements* ✌🏾🙂
|
||||
|
||||
|
||||
# PYPI
|
||||
[PyPI Package](https://pypi.org/project/octosuite)
|
||||
|
||||

|
||||
|
||||
|
||||
# LICENSE
|
||||
## License
|
||||

|
||||
|
||||
|
||||
# ABOUT DEVELOPER
|
||||
## About developer
|
||||
[About.me](https://about.me/rly0nheart)
|
||||
|
||||
|
||||
# SUPPORTERS
|
||||
## Supporters
|
||||
[](https://github.com/rly0nheart/octosuite/stargazers)
|
||||
[](https://github.com/rly0nheart/octosuite/members)
|
||||
|
||||
|
||||
# DONATIONS
|
||||
## Donations
|
||||
Love octosuite? Please consider buying me a coffee, I will really appreciate it. ☕👌🏾😊
|
||||
|
||||
<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>
|
||||
|
||||
736
core/main.py
736
core/main.py
@@ -1,736 +0,0 @@
|
||||
'''
|
||||
OCTOSUITE Advanced Github OSINT Framework
|
||||
Copyright (C) 2022 Richard Mwewa
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
'''
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import getpass
|
||||
import requests
|
||||
import platform
|
||||
import subprocess
|
||||
from tqdm import tqdm
|
||||
from pprint import pprint
|
||||
from utilities.misc import Banner
|
||||
from utilities.helper import Help
|
||||
from utilities.colors import Color
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# logMsg
|
||||
# This class is where the main notification strings/messages are held,
|
||||
# and are being used in two different cases (they're beig used by logging to be written to log files, and being printed out to the screen).
|
||||
class logMsg:
|
||||
Ctrl = 'Session terminated with (Ctrl+C).'
|
||||
Error = 'Session terminated on error: {}'
|
||||
sessionStart = 'Started new session on {}:{}'
|
||||
sessionClosed = 'Session closed with (exit) command.'
|
||||
fileDeleted = 'Deleted log: {}'
|
||||
readingFile = 'Reading log: {}'
|
||||
viewingLogs = 'Viewing logs...'
|
||||
checkingUpdates = 'Checking for update(s)...'
|
||||
installingUpdates = 'Installing update(s)...'
|
||||
installedUpdates = '{} Update(s) installed.'
|
||||
|
||||
|
||||
# firstBlood
|
||||
# *I couldn't think of a good name for this.*
|
||||
# The firstBlood is responsible for creating/checking the availability of the .logs folder
|
||||
# and enabling logging to automatically log network/user activity to a file.
|
||||
class firstBlood:
|
||||
# If .logs folder exists, we ignore (pass)
|
||||
if os.path.exists('.logs'):
|
||||
pass
|
||||
else:
|
||||
# Creating the .logs directory
|
||||
# If the current system is Windows based, we run mkdir command (without sudo?)
|
||||
# Else we run the mkdir command (with sudo)
|
||||
# As of writing, I have absolutely no idea if Windows users also use sudo to run commands as root/admin
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['mkdir','.logs'])
|
||||
else:
|
||||
subprocess.run(['sudo','mkdir','.logs'],shell=False)
|
||||
|
||||
# Set to automatically monitor and log network and user activity into the .logs folder
|
||||
now = datetime.now()
|
||||
now_formatted = now.strftime('%Y-%m-%d %H:%M:%S%p')
|
||||
logging.basicConfig(filename=f'.logs/{now_formatted}.log',format='[%(asctime)s] [%(levelname)s] %(message)s',datefmt='%Y-%m-%d %H:%M:%S%p',level=logging.DEBUG)
|
||||
|
||||
|
||||
# Attributes
|
||||
# *Even here, I couldn't think of a good name.*
|
||||
# The Attributes class holds the signs/symbols that show what a notification in OctoSuite might be all about.
|
||||
# This might not be very important or necessary in some cases, but I think it's better to know the severerity of the notifications you get in a program.
|
||||
class Attributes:
|
||||
prompt = f'\n{Color.white}[{Color.green}?{Color.white}]{Color.reset}'
|
||||
warning = f'\n{Color.white}[{Color.red}!{Color.white}]{Color.reset}'
|
||||
error = f'\n{Color.white}[{Color.red}x{Color.white}]{Color.reset}'
|
||||
positive = f'\n{Color.white}[{Color.green}+{Color.white}]{Color.reset}'
|
||||
negative = f'\n{Color.white}[{Color.red}-{Color.white}]{Color.reset}'
|
||||
info = f'\n{Color.white}[{Color.green}*{Color.white}]{Color.reset}'
|
||||
|
||||
|
||||
# Octosuite
|
||||
# This class is the backbone of the program.
|
||||
# It holds all important methods/information for the program.
|
||||
class Octosuite:
|
||||
def __init__(self):
|
||||
firstBlood()
|
||||
# A list of tuples, mapping commands to their respective functionalities
|
||||
self.commands_map = [('org:events', self.orgEvents),
|
||||
('org:profile', self.orgProfile),
|
||||
('org:repos', self.orgRepos),
|
||||
('org:member', self.orgMember),
|
||||
('repo:pathcontents', self.pathContents),
|
||||
('repo:profile', self.repoProfile),
|
||||
('repo:contributors', self.repoContributors),
|
||||
('repo:languages', self.repoLanguages),
|
||||
('repo:stargazers', self.repoStargazers),
|
||||
('repo:forks', self.repoForks),
|
||||
('repo:releases', self.repoReleases),
|
||||
('user:repos', self.userRepos),
|
||||
('user:gists', self.userGists),
|
||||
('user:orgs', self.userOrgs),
|
||||
('user:profile', self.userProfile),
|
||||
('user:events', self.userEvents),
|
||||
('user:followers', self.userFollowers),
|
||||
('user:following', self.userFollowing),
|
||||
('user:subscriptions', self.userSubscriptions),
|
||||
('search:users', self.userSearch),
|
||||
('search:repos', self.repoSearch),
|
||||
('search:topics', self.topicSearch),
|
||||
('search:issues', self.issueSearch),
|
||||
('search:commits', self.commitsSearch),
|
||||
('logs:view',self.viewLogs),
|
||||
('logs:read',self.readLog),
|
||||
('logs:delete',self.deleteLog),
|
||||
('update:check', self.checkUpdate),
|
||||
('update:install', self.installUpdate),
|
||||
('help', Help.helpCommand),
|
||||
('help:search', Help.searchCommand),
|
||||
('help:user', Help.userCommand),
|
||||
('help:repo', Help.repoCommand),
|
||||
('help:logs', Help.logsCommand),
|
||||
('help:org', Help.orgCommand),
|
||||
('help:update', Help.updateCommand),
|
||||
('author', self.author),
|
||||
('about', self.about),
|
||||
('clear',self.clearScreen),
|
||||
('version', self.versionInfo),
|
||||
('exit', self.exitSession)]
|
||||
|
||||
|
||||
# Path attribute
|
||||
self.path_attrs =['size','type','path','sha','html_url']
|
||||
# Path attribute dictionary
|
||||
self.path_attr_dict = {'size': 'Size (bytes)',
|
||||
'type': 'Type',
|
||||
'path': 'Path',
|
||||
'sha': 'SHA',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Organization attributes
|
||||
self.org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at']
|
||||
# Organization attribute dictionary
|
||||
self.org_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'email': 'Email',
|
||||
'description': 'About',
|
||||
'location': 'Location',
|
||||
'blog': 'Blog',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'type': 'Account type',
|
||||
'is_verified': 'Is verified?',
|
||||
'has_organization_projects': 'Has organization projects?',
|
||||
'has_repository_projects': 'Has repository projects?',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Repository attributes
|
||||
self.repo_attrs = ['id','description','forks','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','fork','allow_forking','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at']
|
||||
# Repository attribute dictionary
|
||||
self.repo_attr_dict = {'id': 'ID#',
|
||||
'description': 'About',
|
||||
'forks': 'Forks',
|
||||
'stargazers_count': 'Stars',
|
||||
'watchers': 'Watchers',
|
||||
'license': 'License',
|
||||
'default_branch': 'Branch',
|
||||
'visibility': 'Visibility',
|
||||
'language': 'Language(s)',
|
||||
'open_issues': 'Open issues',
|
||||
'topics': 'Topics',
|
||||
'homepage': 'Homepage',
|
||||
'clone_url': 'Clone URL',
|
||||
'ssh_url': 'SSH URL',
|
||||
'fork': 'Is fork?',
|
||||
'allow_forking': 'Is forkable?',
|
||||
'private': 'Is private?',
|
||||
'archived': 'Is archived?',
|
||||
'is_template': 'Is template?',
|
||||
'has_wiki': 'Has wiki?',
|
||||
'has_pages': 'Has pages?',
|
||||
'has_projects': 'Has projects?',
|
||||
'has_issues': 'Has issues?',
|
||||
'has_downloads': 'Has downloads?',
|
||||
'pushed_at': 'Pushed at',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Repo releases attributes
|
||||
self.repo_releases_attrs = ['node_id','tag_name','target_commitish','assets','draft','prerelease','created_at','published_at']
|
||||
# Repo releases attribute dictionary
|
||||
self.repo_releases_attr_dict = {'node_id': 'Node ID',
|
||||
'tag_name': 'Tag',
|
||||
'target_commitish': 'Branch',
|
||||
'assets': 'Assets',
|
||||
'draft': 'Is draft?',
|
||||
'prerelease': 'Is prerelease?',
|
||||
'created_at': 'Created at',
|
||||
'published_at': 'Published at'}
|
||||
|
||||
|
||||
# Profile attributes
|
||||
self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at']
|
||||
# Profile attribute dictionary
|
||||
self.profile_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'bio': 'Bio',
|
||||
'blog': 'Blog',
|
||||
'location': 'Location',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'company': 'Organization',
|
||||
'hireable': 'Is hireable?',
|
||||
'site_admin': 'Is site admin?',
|
||||
'created_at': 'Joined at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# User attributes
|
||||
self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url']
|
||||
# User attribute dictionary
|
||||
self.user_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'gravatar_id': 'Gravatar ID',
|
||||
'site_admin': 'Is site admin?',
|
||||
'type': 'Account type',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Topic atrributes
|
||||
self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at']
|
||||
# Topic attribute dictionary
|
||||
self.topic_attr_dict = {'score': 'Score',
|
||||
'curated': 'Curated',
|
||||
'featured': 'Featured',
|
||||
'display_name': 'Display Name',
|
||||
'created_by': 'Created by',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Gists attributes
|
||||
self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at']
|
||||
# Gists attribute dictionary
|
||||
self.gists_attr_dict = {'node_id': 'Node ID',
|
||||
'description': 'About',
|
||||
'comments': 'Comments',
|
||||
'files': 'Files',
|
||||
'git_push_url': 'Git Push URL',
|
||||
'public': 'Is public?',
|
||||
'truncated': 'Is truncated?',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Issue attributes
|
||||
self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body']
|
||||
# Issue attribute dict
|
||||
self.issue_attr_dict = {'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'score': 'Score',
|
||||
'state': 'State',
|
||||
'closed_at': 'Closed at',
|
||||
'number': 'Number',
|
||||
'comments': 'Comments',
|
||||
'milestone': 'Milestone',
|
||||
'assignee': 'Assignee',
|
||||
'assignees': 'Assignees',
|
||||
'labels': 'Labels',
|
||||
'draft': 'Is draft?',
|
||||
'locked': 'Is locked?',
|
||||
'created_at': 'Created at',
|
||||
'body': 'Body'}
|
||||
|
||||
|
||||
# User organizations attributes
|
||||
self.user_orgs_attrs = ['avatar_url','id','node_id','url','description']
|
||||
self.user_orgs_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID#',
|
||||
'node_id': 'Node ID',
|
||||
'url': 'URL',
|
||||
'description': 'About'}
|
||||
|
||||
|
||||
# Author dictionary
|
||||
self.author_dict = {'Alias': 'rly0nheart',
|
||||
'Country': 'Zambia, Africa',
|
||||
'About.me': 'https://about.me/rly0nheart',
|
||||
'BuyMeACoffee': 'https://buymeacoffee.com/189381184'}
|
||||
|
||||
# About dictionary
|
||||
self.about_dict = {'Version': Banner.versionTag,'Category': 'Open Source Intelligence'}
|
||||
|
||||
|
||||
|
||||
def onStart(self):
|
||||
# Log the beginning of a session
|
||||
logging.info(logMsg.sessionStart.format(platform.node(), os.getlogin()))
|
||||
|
||||
# Use 'cls' to clear screen on Windows based machines
|
||||
# Otherwise, use 'clear'
|
||||
while True:
|
||||
command_input = input(f'''\n{Color.white}┌──({Color.red}{getpass.getuser()}{Color.white}@{Color.red}octosuite{Color.white})-[{Color.green}{os.getcwd()}{Color.white}]\n└╼[{Color.green}:_{Color.white}]{Color.reset} ''')
|
||||
# Looping through the commands base to check if the user input command matches any command in the commands base, and return its functionality
|
||||
# If no match is found, we ignore it
|
||||
for command, functionality in self.commands_map:
|
||||
if command_input.lower() == command:
|
||||
functionality()
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
# Fetching organization info
|
||||
def orgProfile(self):
|
||||
organization = input(f'\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in self.org_attrs:
|
||||
print(f'{Color.white}├─ {self.org_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching user information
|
||||
def userProfile(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in self.profile_attrs:
|
||||
print(f'{Color.white}├─ {self.profile_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching repository information
|
||||
def repoProfile(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
print(f"\n{Color.white}{response.json()['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Get path contents
|
||||
def pathContents(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
path_name = input(f'{Color.white}--> ~{Color.green}/path/name{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Information {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.path_attrs:
|
||||
print(f'{Color.white}├─ {self.path_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo contributors
|
||||
def repoContributors(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/contributors')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f'{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo downloads
|
||||
def repoLanguages(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/languages')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository has no supported language(s){Color.reset}')
|
||||
else:
|
||||
for language in response.json():
|
||||
print(language)
|
||||
|
||||
|
||||
# repo stargazers
|
||||
def repoStargazers(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/stargazers')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository does not have stargazers.{Color.reset}')
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f'{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# repo forks
|
||||
def repoForks(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/forks')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user {response.json()['message']}{Color.reset}")
|
||||
elif response.json() == {}:
|
||||
print(f'{Attributes.negative} Repository does not have forks.{Color.reset}')
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f'{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Repo releases
|
||||
def repoReleases(self):
|
||||
repo_name = input(f'\n{Color.white}--> %{Color.green}reponame{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/repos/{username}/{repo_name}/releases')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Repository or user not found{Color.reset}")
|
||||
elif response.json() == []:
|
||||
print(f"\n{Attributes.negative} Repository does not have releases{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.repo_releases_attrs:
|
||||
print(f'{Color.white}├─ {self.repo_releases_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
print(item['body'])
|
||||
|
||||
|
||||
# Fetching organization repositories
|
||||
def orgRepos(self):
|
||||
organization = input(f'\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/repos?per_page=100')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for repo in response.json():
|
||||
print(f"\n{Color.white}{repo['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{repo[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# organization events
|
||||
def orgEvents(self):
|
||||
organization = input(f"\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ")
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/events')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} Organization {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{item['type']}{Color.reset}\n{Color.white}├─ Created at: {Color.green}{item['created_at']}{Color.green}")
|
||||
pprint(item['payload'])
|
||||
print(f"{Color.reset}\n")
|
||||
|
||||
|
||||
# organization member
|
||||
def orgMember(self):
|
||||
organization = input(f"\n{Color.white}--> @{Color.green}organization{Color.white} (username){Color.reset} ")
|
||||
username = input(f'{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/orgs/{organization}/public_members/{username}')
|
||||
if response.status_code == 204:
|
||||
print(f"{Attributes.positive} User is a public member of the organization{Color.reset}")
|
||||
else:
|
||||
print(f"{Attributes.negative} {response.json()['message']}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user repositories
|
||||
def userRepos(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/repos?per_page=100')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for repo in response.json():
|
||||
print(f"\n{Color.white}{repo['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{repo[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user's gists
|
||||
def userGists(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/gists')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not have any active gists{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
for attr in self.gists_attrs:
|
||||
print(f"{Color.white}├─ {self.gists_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching a list of organizations that a user owns or belongs to
|
||||
def userOrgs(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/orgs')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not belong to or own any organizations.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f'\n{Color.white}{item["login"]}{Color.reset}')
|
||||
for attr in self.user_orgs_attrs:
|
||||
print(f'{Color.white}├─ {self.user_orgs_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}')
|
||||
|
||||
|
||||
# Fetching a users events
|
||||
def userEvents(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/events/public')
|
||||
if response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{item['type']}{Color.reset}\n{Color.white}├─ Created at: {Color.green}{item['created_at']}{Color.green}")
|
||||
pprint(item['payload'])
|
||||
print(reset)
|
||||
|
||||
|
||||
# Fetching a target user's subscriptions
|
||||
def userSubscriptions(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/subscriptions')
|
||||
if response.json() == []:
|
||||
print(f"{Attributes.negative} User does not have any subscriptions.{Color.reset}")
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Fetching user's followera'
|
||||
def userFollowers(self):
|
||||
username = input(f'\n{Color.white}--> @{Color.green}username{Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{username}/followers?per_page=100')
|
||||
if response.json() == []:
|
||||
print(f'{Attributes.negative} User does not have followers.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
print(f"{Attributes.negative} User {response.json()['message']}{Color.reset}")
|
||||
else:
|
||||
for item in response.json():
|
||||
print(f"\n{Color.white}@{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Checking whether or not user[A] follows user[B]
|
||||
def userFollowing(self):
|
||||
user_a = input(f'\n{Color.white}--> @{Color.green}user{Color.white}[A] (username){Color.reset} ')
|
||||
user_b = input(f'{Color.white}--> @{Color.green}user{Color.white}[B] (username){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/users/{user_a}/following/{user_b}')
|
||||
if response.status_code == 204:
|
||||
print(f'{Attributes.positive} @{user_a} follows @{user_b}{Color.reset}')
|
||||
else:
|
||||
print(f'{Attributes.negative} @{user_a} does not follow @{user_b}{Color.reset}')
|
||||
|
||||
|
||||
# User search
|
||||
def userSearch(self):
|
||||
query = input(f'\n{Color.white}--> @{Color.green}query{Color.white} (eg. john){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/users?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}@{item['login']}{Color.reset}")
|
||||
for attr in self.user_attrs:
|
||||
print(f"{Color.white}├─ {self.user_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Repository search
|
||||
def repoSearch(self):
|
||||
query = input(f'\n{Color.white}--> %{Color.green}query{Color.white} (eg. git){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/repositories?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['full_name']}{Color.reset}")
|
||||
for attr in self.repo_attrs:
|
||||
print(f"{Color.white}├─ {self.repo_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Topics search
|
||||
def topicSearch(self):
|
||||
query = input(f'\n{Color.white}--> #{Color.green}query{Color.white} (eg. osint){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/topics?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['name']}{Color.reset}")
|
||||
for attr in self.topic_attrs:
|
||||
print(f"{Color.white}├─ {self.topic_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Issue search
|
||||
def issueSearch(self):
|
||||
query = input(f'\n{Color.white}--> !{Color.green}query{Color.white} (eg. error){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/issues?q={query}&per_page=100').json()
|
||||
for item in response['items']:
|
||||
print(f"\n{Color.white}{item['title']}{Color.reset}")
|
||||
for attr in self.issue_attrs:
|
||||
print(f"{Color.white}├─ {self.issue_attr_dict[attr]}: {Color.green}{item[attr]}{Color.reset}")
|
||||
|
||||
|
||||
# Commits search
|
||||
def commitsSearch(self):
|
||||
query = input(f'\n{Color.white}--> :{Color.green}query{Color.white} (eg. filename:index.php){Color.reset} ')
|
||||
response = requests.get(f'https://api.github.com/search/commits?q={query}&per_page=100').json()
|
||||
number=0
|
||||
for item in response['items']:
|
||||
number+=1
|
||||
print(f'\n{Color.white}-> {number}.{Color.reset}')
|
||||
pprint(item['commit'])
|
||||
|
||||
|
||||
# View octosuite log files
|
||||
def viewLogs(self):
|
||||
logging.info(logMsg.viewingLogs)
|
||||
logs = os.listdir('.logs')
|
||||
print(f'''\n{Color.white}Log Size{Color.reset}
|
||||
--- ---------''')
|
||||
for log in logs:
|
||||
print(f"{log}\t ",os.path.getsize(".logs/"+log),"bytes")
|
||||
|
||||
|
||||
# Delete a specified log file
|
||||
def deleteLog(self):
|
||||
log_file = input(f"\n{Color.white}--> logfile (eg. 2022-04-27 10:09:36AM.log){Color.reset} ")
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['del',f'{os.getcwd()}/.logs/{log_file}'])
|
||||
else:
|
||||
subprocess.run(['sudo','rm',f'.logs/{log_file}'],shell=False)
|
||||
|
||||
logging.info(logMsg.fileDeleted.format(log_file))
|
||||
print(Attributes.positive, logMsg.fileDeleted)
|
||||
|
||||
|
||||
# Read a specified log file
|
||||
def readLog(self):
|
||||
log_file = input(f"\n{Color.white}--> logfile (eg. 2022-04-27 10:09:36AM.log){Color.reset} ")
|
||||
with open(f'.logs/{log_file}', 'r') as log:
|
||||
logging.info(logMsg.readingFile.format(log_file))
|
||||
print("\n"+log.read())
|
||||
|
||||
|
||||
# Update program
|
||||
def installUpdate(self):
|
||||
files_to_update = ['core/main.py','utilities/helper.py','utilities/misc.py','utilities/colors.py','octosuite','.github/dependabot.yml','.github/ISSUE_TEMPLATE/bug_report.md','.github/ISSUE_TEMPLATE/feature_request.md','.github/ISSUE_TEMPLATE/config.yml','LICENSE','README.md','requirements.txt']
|
||||
logging.info(logMsg.installingUpdates)
|
||||
for file in tqdm(files_to_update,desc = logMsg.installingUpdates):
|
||||
data = requests.get(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}')
|
||||
with open(file, 'wb') as code:
|
||||
code.write(data.content)
|
||||
code.close()
|
||||
|
||||
logging.info(logMsg.installedUpdates.format(len(files_to_update)))
|
||||
print(Attributes.positive, logMsg.installedUpdates.format(len(files_to_update)));exit()
|
||||
|
||||
|
||||
def checkUpdate(self):
|
||||
logging.info(logMsg.checkingUpdates)
|
||||
response = requests.get("https://api.github.com/repos/rly0nheart/octosuite/releases/latest")
|
||||
if response.json()['tag_name'] == Banner.versionTag:
|
||||
print(f"{Attributes.positive} OctoSuite is up to date. Check again soon :)")
|
||||
else:
|
||||
print(f"{Attributes.info} A new release is available ({response.json()['tag_name']}). Use command {Color.green}update:install{Color.white} to download and install the updates.{Color.reset}")
|
||||
|
||||
|
||||
# Show version information
|
||||
def versionInfo(self):
|
||||
# Yes... the changelog is actually hard coded
|
||||
# It's actually frustrating having to change this everytime I publish a new release lol
|
||||
print(f'''
|
||||
Tag: {Banner.versionTag}
|
||||
Released at: 2022-05-25 11:05AM
|
||||
{'-'*31}
|
||||
|
||||
What's changed?
|
||||
{'-'*15}
|
||||
[✓] Fixed a bug in issue #2''')
|
||||
|
||||
|
||||
# Author info
|
||||
def author(self):
|
||||
print(f'\n{Color.white}Richard Mwewa (Ritchie){Color.reset}')
|
||||
for key,value in self.author_dict.items():
|
||||
print(f'{Color.white}├─ {key}: {Color.green}{value}{Color.reset}')
|
||||
|
||||
|
||||
def about(self):
|
||||
print('''
|
||||
OCTOSUITE (C) 2022 Richard Mwewa
|
||||
|
||||
is an advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations.''')
|
||||
|
||||
|
||||
# Close session
|
||||
def exitSession(self):
|
||||
logging.info(logMsg.sessionClosed)
|
||||
print(Attributes.info, logMsg.sessionClosed);exit()
|
||||
|
||||
|
||||
def clearScreen(self):
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['cls'])
|
||||
else:
|
||||
subprocess.run(['clear'],shell=False)
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
19
octosuite
19
octosuite
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
from utilities.colors import Color
|
||||
from utilities.misc import Banner
|
||||
from core.main import Octosuite, Attributes, logMsg
|
||||
|
||||
print(Banner.nameLogo)
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
Octosuite().onStart()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning(logMsg.Ctrl)
|
||||
print(Attributes.warning, logMsg.Ctrl)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(logMsg.Error.format(e))
|
||||
print(Attributes.error, logMsg.Error.format(f'{Color.red}{e}{Color.reset}'))
|
||||
23
octosuite/banner.py
Normal file
23
octosuite/banner.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import getpass
|
||||
from octosuite.colors import Color
|
||||
|
||||
'''
|
||||
Banner
|
||||
This class holds the program's banner logo and version tag
|
||||
'''
|
||||
class Banner:
|
||||
versionTag = '2.1.1'
|
||||
nameLogo = f'''{Color.white}
|
||||
_______ __ _______ __ __
|
||||
| |.----.| |_.-----.| __|.--.--.|__| |_.-----.
|
||||
| - || __|| _| _ ||__ || | || | _| -__|
|
||||
|_______||____||____|_____||_______||_____||__|____|_____|
|
||||
v{versionTag}
|
||||
{Color.white}— Advanced Github {Color.red}OSINT{Color.white} Framework{Color.reset}
|
||||
|
||||
|
||||
|
||||
.:{getpass.getuser()}:.
|
||||
{Color.white}├─ use {Color.green}help{Color.reset}{Color.white} command for usage{Color.reset}
|
||||
{Color.white}└╼ commands are case insensitive{Color.reset}
|
||||
'''
|
||||
@@ -1,5 +1,4 @@
|
||||
import sys
|
||||
import getpass
|
||||
import platform
|
||||
from datetime import datetime
|
||||
|
||||
@@ -21,10 +20,10 @@ class Color:
|
||||
# But at least users will get to know things they did not know about their machines ;)
|
||||
date_time = datetime.now()
|
||||
sys_info = [("Processor",platform.processor),
|
||||
("Node", platform.node),
|
||||
("Release", platform.release),
|
||||
("Architecture", platform.architecture),
|
||||
("Version", platform.version)]
|
||||
("Node", platform.node),
|
||||
("Release", platform.release),
|
||||
("Architecture", platform.architecture),
|
||||
("Version", platform.version)]
|
||||
|
||||
banner = f"""
|
||||
OCTOSUITE © 2022 Richard Mwewa
|
||||
@@ -39,13 +38,12 @@ class Color:
|
||||
print("\n")
|
||||
while True:
|
||||
try:
|
||||
color_chooser = input(f"[ ? ] Welcome {getpass.getuser()}, would you like to enable colors for this session? (y/n) ")
|
||||
color_chooser = input(f"[ ? ] Welcome, would you like to enable colors for this session? (Y/n) ")
|
||||
if color_chooser.lower() == "y":
|
||||
white = "\033[97m"
|
||||
red = "\033[91m"
|
||||
reset = "\033[0m"
|
||||
green = "\033[92m"
|
||||
red_bg = "\033[41;37m"
|
||||
break
|
||||
elif color_chooser.lower() == "n":
|
||||
red = white = green = red_bg = reset = ""
|
||||
@@ -54,4 +52,4 @@ class Color:
|
||||
print(f"\n[ ! ] Your response ({color_chooser}) is invalid (expected y or n) ")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
exit(f"[ ! ] Process interrupted with (Ctrl+C).")
|
||||
exit(f"[ ! ] Process interrupted with [Ctrl+C].")
|
||||
446
octosuite/csv_loggers.py
Normal file
446
octosuite/csv_loggers.py
Normal file
@@ -0,0 +1,446 @@
|
||||
import csv
|
||||
import sys
|
||||
import logging
|
||||
from octosuite.sign_vars import SignVar
|
||||
from octosuite.log_roller import logRoller
|
||||
|
||||
'''
|
||||
csvLogger
|
||||
This class holds the methods for creating .csv files of each functionality in main
|
||||
'''
|
||||
class csvLogger:
|
||||
# .csv for organization' profile
|
||||
def logOrgProfile(response):
|
||||
org_profile_fields = ['Profile photo', 'Name', 'Username', 'ID', 'Node ID', 'Email', 'About', 'Location', 'Blog', 'Followers', 'Following', 'Twitter handle', 'Gists', 'Repositories', 'Account type', 'Is verified?', 'Has organization projects?', 'Has repository projects?', 'Created at', 'Updated at']
|
||||
org_profile_row = [response.json()['avatar_url'], response.json()['name'], response.json()['login'], response.json()['id'], response.json()['node_id'], response.json()['email'], response.json()['description'], response.json()['location'], response.json()['blog'], response.json()['followers'], response.json()['following'], response.json()['twitter_username'], response.json()['public_gists'], response.json()['public_repos'], response.json()['type'], response.json()['is_verified'], response.json()['has_organization_projects'], response.json()['has_repository_projects'], response.json()['created_at'], response.json()['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{response.json()['name']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(org_profile_fields)
|
||||
writecsv.writerow(org_profile_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Creating a .csv file of a user' profile
|
||||
def logUserProfile(response):
|
||||
user_profile_fields = ['Profile photo', 'Name', 'Username', 'ID', 'Node ID', 'Bio', 'Blog', 'Location', 'Followers', 'Following', 'Twitter handle', 'Gists', 'Repositories', 'Organization', 'Is hireable?', 'Is site admin?', 'Joined at', 'Updated at']
|
||||
user_profile_row = [response.json()['avatar_url'], response.json()['name'], response.json()['login'], response.json()['id'], response.json()['node_id'], response.json()['bio'], response.json()['blog'], response.json()['location'], response.json()['followers'], response.json()['following'], response.json()['twitter_username'], response.json()['public_gists'], response.json()['public_repos'], response.json()['company'], response.json()['hireable'], response.json()['site_admin'], response.json()['created_at'], response.json()['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{response.json()['login']}.csv", 'w',) as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_profile_fields)
|
||||
writecsv.writerow(user_profile_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository profile
|
||||
def logRepoProfile(response):
|
||||
repo_profile_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
repo_profile_row = [response.json()['name'], response.json()['id'], response.json()['description'], response.json()['forks'], response.json()['stargazers_count'], response.json()['watchers'], response.json()['license'], response.json()['default_branch'], response.json()['visibility'], response.json()['language'], response.json()['open_issues'], response.json()['topics'], response.json()['homepage'], response.json()['clone_url'], response.json()['ssh_url'], response.json()['fork'], response.json()['allow_forking'], response.json()['private'], response.json()['archived'], response.json()['is_template'], response.json()['has_wiki'], response.json()['has_pages'], response.json()['has_projects'], response.json()['has_issues'], response.json()['has_downloads'], response.json()['pushed_at'], response.json()['created_at'], response.json()['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{response.json()['name']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_profile_fields)
|
||||
writecsv.writerow(repo_profile_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository path contents
|
||||
def logRepoPathContents(content, repo_name):
|
||||
path_content_fields = ['Filename', 'Size (bytes)', 'Type', 'Path', 'SHA', 'URL']
|
||||
path_content_row = [content['name'], content['size'], content['type'], content['path'], content['sha'], content['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{content['name']}_content_from_{repo_name}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(path_content_fields)
|
||||
writecsv.writerow(path_content_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository stargazer
|
||||
def logRepoStargazers(stargazer, repo_name):
|
||||
user_follower_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL']
|
||||
user_follower_row = [stargazer['avatar_url'], stargazer['login'], stargazer['id'], stargazer['node_id'], stargazer['gravatar_id'], stargazer['type'], stargazer['site_admin'], stargazer['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{stargazer['login']}_stargazer_of_{repo_name}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_follower_fields)
|
||||
writecsv.writerow(user_follower_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository forks
|
||||
def logRepoForks(fork, count):
|
||||
repo_fork_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
repo_fork_row = [fork['full_name'], fork['id'], fork['description'], fork['forks'], fork['stargazers_count'], fork['watchers'], fork['license'], fork['default_branch'], fork['visibility'], fork['language'], fork['open_issues'], fork['topics'], fork['homepage'], fork['clone_url'], fork['ssh_url'], fork['fork'], fork['allow_forking'], fork['private'], fork['archived'], fork['is_template'], fork['has_wiki'], fork['has_pages'], fork['has_projects'], fork['has_issues'], fork['has_downloads'], fork['pushed_at'], fork['created_at'], fork['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{fork['name']}_fork_{count}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_fork_fields)
|
||||
writecsv.writerow(repo_fork_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository issues
|
||||
def logRepoIssues(issue, repo_name):
|
||||
repo_issue_fields = ['Title', 'ID', 'Node ID', 'Number', 'State', 'Reactions', 'Comments', 'Milestone', 'Assignee', 'Assignees', 'Author association', 'Labels', 'Is locked?', 'Lock reason', 'Closed at', 'Created at', 'Updated at']
|
||||
repo_issue_row = [issue['title'], issue['id'], issue['node_id'], issue['number'], issue['state'], issue['reactions'], issue['comments'], issue['milestone'], issue['assignee'], issue['assignees'], issue['author_association'], issue['labels'], issue['locked'], issue['active_lock_reason'], issue['closed_at'], issue['created_at'], issue['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{repo_name}_issue_{issue['id']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_issue_fields)
|
||||
writecsv.writerow(repo_issue_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# create .csv for repository releases
|
||||
def logRepoReleases(release, repo_name):
|
||||
repo_release_fields = ['Name', 'ID', 'Node ID', 'Tag', 'Branch', 'Assets', 'Is draft?', 'Is prerelease?', 'Created at', 'Published at']
|
||||
repo_release_row = [release['name'], release['id'], release['node_id'], release['tag_name'], release['target_commitish'], release['assets'], release['draft'], release['prerelease'], release['created_at'], release['published_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{repo_name}_release_{release['name']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_release_fields)
|
||||
writecsv.writerow(repo_release_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv file for repository contributors
|
||||
def logRepoContributors(contributor, repo_name):
|
||||
repo_contributor_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL']
|
||||
repo_contributor_row = [contributor['avatar_url'], contributor['login'], contributor['id'], contributor['node_id'], contributor['gravatar_id'], contributor['type'], contributor['site_admin'], contributor['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{contributor['login']}_contributor_of_{repo_name}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_contributor_fields)
|
||||
writecsv.writerow(repo_contributor_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
print(f"{SignVar.info} {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for organization' events
|
||||
def logOrgEvents(event, organization):
|
||||
org_event_fields = ['ID', 'Type', 'Created at', 'Payload']
|
||||
org_event_row = [event['id'], event['type'], event['created_at'], event['payload']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{organization}_event_{event['id']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(org_event_fields)
|
||||
writecsv.writerow(org_event_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for organization' repositories
|
||||
def logOrgRepos(repository, organization):
|
||||
org_repo_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
org_repo_row = [repository['full_name'], repository['id'], repository['description'], repository['forks'], repository['stargazers_count'], repository['watchers'], repository['license'], repository['default_branch'], repository['visibility'], repository['language'], repository['open_issues'], repository['topics'], repository['homepage'], repository['clone_url'], repository['ssh_url'], repository['fork'], repository['allow_forking'], repository['private'], repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{repository['name']}_repository_of_{organization}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(org_repo_fields)
|
||||
writecsv.writerow(org_repo_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user' repositories
|
||||
def logUserRepos(repository, username):
|
||||
user_repo_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
user_repo_row = [repository['full_name'], repository['id'], repository['description'], repository['forks'], repository['stargazers_count'], repository['watchers'], repository['license'], repository['default_branch'], repository['visibility'], repository['language'], repository['open_issues'], repository['topics'], repository['homepage'], repository['clone_url'], repository['ssh_url'], repository['fork'], repository['allow_forking'], repository['private'], repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{repository['name']}_{username}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_repo_fields)
|
||||
writecsv.writerow(user_repo_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user events
|
||||
def logUserEvents(event):
|
||||
user_event_fields = ['Actor', 'Type', 'Repository', 'Created at', 'Payload']
|
||||
user_event_row = [event['actor']['login'], event['type'], event['repo']['name'], event['created_at'], event['payload']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{event['actor']['login']}_event_{event['id']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_event_fields)
|
||||
writecsv.writerow(user_event_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user gists
|
||||
def logUserGists(gist):
|
||||
user_gist_fields = ['ID', 'Node ID', 'About', 'Comments', 'Files', 'Git Push URL', 'Is public?', 'Is truncated?', 'Updated at']
|
||||
user_gist_row = [gist['id'], gist['node_id'], gist['description'], gist['comments'], gist['files'], gist['git_push_url'], gist['public'], gist['truncated'], gist['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{gist['id']}_gists_{gist['owner']['login']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_gist_fields)
|
||||
writecsv.writerow(user_gist_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user followers
|
||||
def logUserFollowers(follower, username):
|
||||
user_follower_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL']
|
||||
user_follower_row = [follower['avatar_url'], follower['login'], follower['id'], follower['node_id'], follower['gravatar_id'], follower['type'], follower['site_admin'], follower['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{follower['login']}_follower_of_{username}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_follower_fields)
|
||||
writecsv.writerow(user_follower_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user following
|
||||
def logUserFollowing(user, username):
|
||||
user_following_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL']
|
||||
user_following_row = [user['avatar_url'], user['login'], user['id'], user['node_id'], user['gravatar_id'], user['type'], user['site_admin'], user['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{user['login']}_followed_by_{username}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_following_fields)
|
||||
writecsv.writerow(user_following_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user' subscriptions
|
||||
def logUserSubscriptions(repository, username):
|
||||
user_subscription_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
user_subscription_row = [repository['name'], repository['id'], repository['description'], repository['forks'], repository['stargazers_count'], repository['watchers'], repository['license'], repository['default_branch'], repository['visibility'], repository['language'], repository['open_issues'], repository['topics'], repository['homepage'], repository['clone_url'], repository['ssh_url'], repository['fork'], repository['allow_forking'], repository['private'], repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{username}_subscriptions_{repository['name']}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_subscription_fields)
|
||||
writecsv.writerow(user_subscription_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# .csv for user organizations
|
||||
def logUserOrgs(organization, username):
|
||||
user_org_fields = ['Profile photo', 'Name', 'ID', 'Node ID', 'URL', 'About']
|
||||
user_org_row = [organization['avatar_url'], organization['login'], organization['id'], organization['node_id'], organization['url'], organization['description']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{organization['login']}_{username}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_org_fields)
|
||||
writecsv.writerow(user_org_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for user search
|
||||
def logUserSearch(user, query):
|
||||
user_search_fields = ['Profile photo', 'Username', 'ID', 'Node ID', 'Gravatar ID', 'Account type', 'Is site admin?', 'URL']
|
||||
user_search_row = [user['avatar_url'], user['login'], user['id'], user['node_id'], user['gravatar_id'], user['type'], user['site_admin'], user['html_url']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{user['login']}_user_search_result_for_{query}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(user_search_fields)
|
||||
writecsv.writerow(user_search_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for repository search
|
||||
def logRepoSearch(repository, query):
|
||||
repo_search_fields = [ 'Name','ID', 'About', 'Forks', 'Stars', 'Watchers', 'License', 'Branch', 'Visibility', 'Language(s)', 'Open issues', 'Topics', 'Homepage', 'Clone URL', 'SSH URL', 'Is fork?', 'Is forkable?', 'Is private?', 'Is archived?', 'Is template?', 'Has wiki?', 'Has pages?', 'Has projects?', 'Has issues?', 'Has downloads?', 'Pushed at', 'Created at', 'Updated at']
|
||||
repo_search_row = [repository['full_name'], repository['id'], repository['description'], repository['forks'], repository['stargazers_count'], repository['watchers'], repository['license'], repository['default_branch'], repository['visibility'], repository['language'], repository['open_issues'], repository['topics'], repository['homepage'], repository['clone_url'], repository['ssh_url'], repository['fork'], repository['allow_forking'], repository['private'], repository['archived'], repository['is_template'], repository['has_wiki'], repository['has_pages'], repository['has_projects'], repository['has_issues'], repository['has_downloads'], repository['pushed_at'], repository['created_at'], repository['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{repository['name']}_repository_search_result_for_{query}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(repo_search_fields)
|
||||
writecsv.writerow(repo_search_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for topic search
|
||||
def logTopicSearch(topic, query):
|
||||
topic_search_fields = ['Name', 'Score', 'Curated', 'Featured', 'Display name', 'Created by', 'Created at', 'Updated at']
|
||||
topic_search_row = [topic['name'], topic['score'], topic['curated'], topic['featured'], topic['display_name'], topic['created_by'], topic['created_at'], topic['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{topic['name']}_topic_search_result_for_{query}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(topic_search_fields)
|
||||
writecsv.writerow(topic_search_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for issues search
|
||||
def logIssueSearch(issue, query):
|
||||
issue_search_fields = ['Title', 'ID', 'Node ID', 'Number', 'State', 'Reactions', 'Comments', 'Milestone', 'Assignee', 'Assignees', 'Author association', 'Labels', 'Is locked?', 'Lock reason', 'Closed at', 'Created at', 'Updated at']
|
||||
issue_search_row = [issue['title'], issue['id'], issue['node_id'], issue['number'], issue['state'], issue['reactions'], issue['comments'], issue['milestone'], issue['assignee'], issue['assignees'], issue['author_association'], issue['labels'], issue['locked'], issue['active_lock_reason'], issue['closed_at'], issue['created_at'], issue['updated_at']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{issue['id']}_issue_search_result_for_{query}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(issue_search_fields)
|
||||
writecsv.writerow(issue_search_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
|
||||
|
||||
# Create .csv for commits search
|
||||
def logCommitsSearch(commit, query):
|
||||
commit_search_fields = ['SHA', 'Author', 'Username', 'Email', 'Committer', 'Repository', 'URL', 'Description']
|
||||
commit_search_row = [commit['commit']['tree']['sha'], commit['commit']['author']['name'], commit['author']['login'], commit['commit']['author']['email'], commit['commit']['committer']['name'], commit['repository']['full_name'], commit['html_url'], commit['commit']['message']]
|
||||
prompt = input(f'\n{SignVar.prompt} {logRoller.askLogCsv}').lower()
|
||||
if prompt == 'y':
|
||||
with open(f"output/{commit['commit']['tree']['sha']}_commit_search_result_for_{query}.csv", 'w') as file:
|
||||
writecsv = csv.writer(file)
|
||||
writecsv.writerow(commit_search_fields)
|
||||
writecsv.writerow(commit_search_row)
|
||||
|
||||
logging.info(logRoller.loggedToCsv.format(file.name))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.loggedToCsv.format(file.name)}\n")
|
||||
|
||||
else:
|
||||
logging.info(logRoller.loggingSkipped)
|
||||
sys.stdout.write(f"{SignVar.info} ({prompt}) {logRoller.loggingSkipped}\n")
|
||||
231
octosuite/helper.py
Normal file
231
octosuite/helper.py
Normal file
@@ -0,0 +1,231 @@
|
||||
import sys
|
||||
from octosuite.colors import Color
|
||||
|
||||
"""
|
||||
Help
|
||||
This class holds the help text for available.
|
||||
Almost everything in the methods from this class is hard coded
|
||||
"""
|
||||
class Help:
|
||||
usageText = 'Use {} to get started'
|
||||
usageText1 = 'Use {} to view all available subcommands.'
|
||||
|
||||
def Org():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-------
|
||||
The '{Color.green}org{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:org')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def Repo():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}repo{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:repo')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def User():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}user{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:user')}{Color.reset}
|
||||
""")
|
||||
|
||||
def Search():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}search{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:search')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def Source():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}source{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:source')}{Color.reset}
|
||||
""")
|
||||
|
||||
def Logs():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}logs{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:logs')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def Version():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}version{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:version')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def Csv():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Note:
|
||||
-----
|
||||
The '{Color.green}csv{Color.white}' command works with subcommands.
|
||||
{Help.usageText1.format('help:csv')}{Color.reset}
|
||||
""")
|
||||
|
||||
|
||||
def versionCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Version subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('version:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
check Check for new release(s)
|
||||
info Version info
|
||||
""")
|
||||
|
||||
|
||||
def sourceCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Source subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('source:<subcommand>')}
|
||||
{Color.white}Command Description{Color.reset}
|
||||
-------- -----------
|
||||
zipball Download source code as zipball
|
||||
tarball Download source code as tarball
|
||||
""")
|
||||
|
||||
|
||||
def searchCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Search subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('search:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
users Search user(s)
|
||||
repos Search repositor[yies]
|
||||
topics Search topic(s)
|
||||
issues Search issue(s)
|
||||
commits Search commit(s)
|
||||
""")
|
||||
|
||||
|
||||
def userCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}User subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('user:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
profile Get a user's profile info
|
||||
gists Return a users's gists
|
||||
orgs Return organizations that a user belongs to/owns
|
||||
repos Return a user's repositories
|
||||
events Return a user's events
|
||||
follows Check if user(A) follows user(B)
|
||||
followers Return a user's followers
|
||||
following Return a list of users the target is following
|
||||
subscriptions Return a user's subscriptions
|
||||
""")
|
||||
|
||||
|
||||
def orgCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Org subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('org:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
profile Get an organization's info
|
||||
repos Return an organization's repositories
|
||||
events Return an organization's events
|
||||
member Check if a specified user is a public member of the target organization
|
||||
""")
|
||||
|
||||
|
||||
def repoCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Repo subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('repo:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
profile Get a repository's info
|
||||
issues Return a repository's issues
|
||||
forks Return a repository's forks
|
||||
releases Return a repository's releases
|
||||
stargazers Return a repository's stargazers
|
||||
pathcontents List contents in a path of a repository
|
||||
""")
|
||||
|
||||
|
||||
def logsCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Logs subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('logs:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
view View logs
|
||||
read Read log
|
||||
delete Delete log
|
||||
""")
|
||||
|
||||
def csvCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Csv subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('csv:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
view View csv files
|
||||
read Read csv
|
||||
delete Delete csv
|
||||
""")
|
||||
|
||||
|
||||
def helpCommand():
|
||||
sys.stdout.write(f"""
|
||||
{Color.white}Core commands{Color.reset}
|
||||
{'='*13}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
help Help menu
|
||||
exit Close session
|
||||
clear Clear screen
|
||||
about Program' info
|
||||
author Developer' info
|
||||
|
||||
|
||||
{Color.white}Help subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('help:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
csv (coming soon)
|
||||
org List all organization investigation commands
|
||||
logs List all logs management commands
|
||||
repo List all repository investigation commands
|
||||
user List all users investigation commands
|
||||
search List all target discovery commands
|
||||
source (beta) List all source code download commands (for developers)
|
||||
version List all version management commands
|
||||
""")
|
||||
25
octosuite/log_roller.py
Normal file
25
octosuite/log_roller.py
Normal file
@@ -0,0 +1,25 @@
|
||||
'''
|
||||
logRoller
|
||||
This class is where the main notification strings/messages are held,
|
||||
and are being used in two different cases (they're beig used by logging to be written to log files, and being printed out to the screen).
|
||||
'''
|
||||
class logRoller:
|
||||
Ctrl = 'Session terminated with {}.'
|
||||
Error = 'An error occurred: {}'
|
||||
sessionOpened = 'Opened new session on {}:{}'
|
||||
sessionClosed = 'Session closed at {}.'
|
||||
deletedLog = 'Deleted log: {}'
|
||||
readingLog = 'Reading log: {}'
|
||||
viewingLogs = 'Viewing logs...'
|
||||
fileDownloading = 'Downloading:{}'
|
||||
fileDownloaded = 'Downloaded:downloads/{}'
|
||||
fileNotFound = 'File ({}) not found.'
|
||||
infoNotFound = 'Information ({} - {} - {}) not found.'
|
||||
repoNotFound = 'Repository ({}) not found.'
|
||||
userNotFound = 'User (@{}) not found.'
|
||||
orgNotFound = 'Organization (@{}) not found.'
|
||||
repoOrUserNotFound = 'Repository or user not found ({} - @{}).'
|
||||
askLogCsv = 'Do you wish to log this output to a .csv file? (Y/n) '
|
||||
loggedToCsv = 'Output logged to {}'
|
||||
loggingSkipped = '.csv logging skipped.'
|
||||
limitInput = ' Limit {} output to how many? (1-100) '
|
||||
941
octosuite/main.py
Normal file
941
octosuite/main.py
Normal file
@@ -0,0 +1,941 @@
|
||||
#!usr/bin/python
|
||||
|
||||
import os
|
||||
import csv
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
import getpass
|
||||
import requests
|
||||
import platform
|
||||
import subprocess
|
||||
from pprint import pprint
|
||||
from datetime import datetime
|
||||
from octosuite.helper import Help
|
||||
from octosuite.colors import Color
|
||||
from octosuite.banner import Banner
|
||||
from octosuite.sign_vars import SignVar
|
||||
from octosuite.log_roller import logRoller
|
||||
from octosuite.csv_loggers import csvLogger
|
||||
|
||||
|
||||
global endpoint
|
||||
global path_attrs
|
||||
global path_attr_dict
|
||||
global org_attrs
|
||||
global org_attr_dict
|
||||
global repo_attrs
|
||||
global repo_attr_dict
|
||||
global repo_releases_attrs
|
||||
global repo_releases_attr_dict
|
||||
global profile_attrs
|
||||
global profile_attr_dict
|
||||
global user_attrs
|
||||
global user_attr_dict
|
||||
global topic_attrs
|
||||
global topic_attr_dict
|
||||
global gists_attrs
|
||||
global gists_attr_dict
|
||||
global issue_attrs
|
||||
global issue_attr_dict
|
||||
global repo_issues_attrs
|
||||
global repo_issues_attr_dict
|
||||
global user_orgs_attrs
|
||||
global user_orgs_attr_dict
|
||||
global author_dict
|
||||
|
||||
|
||||
# API endpoint
|
||||
endpoint = 'https://api.github.com'
|
||||
# Path attribute
|
||||
path_attrs =['size','type','path','sha','html_url']
|
||||
# Path attribute dictionary
|
||||
path_attr_dict = {'size': 'Size (bytes)',
|
||||
'type': 'Type',
|
||||
'path': 'Path',
|
||||
'sha': 'SHA',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Organization attributes
|
||||
org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at']
|
||||
# Organization attribute dictionary
|
||||
org_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'email': 'Email',
|
||||
'description': 'About',
|
||||
'location': 'Location',
|
||||
'blog': 'Blog',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter handle',
|
||||
'public_gists': 'Gists',
|
||||
'public_repos': 'Repositories',
|
||||
'type': 'Account type',
|
||||
'is_verified': 'Is verified?',
|
||||
'has_organization_projects': 'Has organization projects?',
|
||||
'has_repository_projects': 'Has repository projects?',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# 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']
|
||||
# Repository attribute dictionary
|
||||
repo_attr_dict = {'id': 'ID',
|
||||
'description': 'About',
|
||||
'forks': 'Forks',
|
||||
'stargazers_count': 'Stars',
|
||||
'watchers': 'Watchers',
|
||||
'license': 'License',
|
||||
'default_branch': 'Branch',
|
||||
'visibility': 'Visibility',
|
||||
'language': 'Language(s)',
|
||||
'open_issues': 'Open issues',
|
||||
'topics': 'Topics',
|
||||
'homepage': 'Homepage',
|
||||
'clone_url': 'Clone URL',
|
||||
'ssh_url': 'SSH URL',
|
||||
'fork': 'Is fork?',
|
||||
'allow_forking': 'Is forkable?',
|
||||
'private': 'Is private?',
|
||||
'archived': 'Is archived?',
|
||||
'is_template': 'Is template?',
|
||||
'has_wiki': 'Has wiki?',
|
||||
'has_pages': 'Has pages?',
|
||||
'has_projects': 'Has projects?',
|
||||
'has_issues': 'Has issues?',
|
||||
'has_downloads': 'Has downloads?',
|
||||
'pushed_at': 'Pushed at',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Repo releases attributes
|
||||
repo_releases_attrs = ['id', 'node_id','tag_name','target_commitish','assets','draft','prerelease','created_at','published_at']
|
||||
# Repo releases attribute dictionary
|
||||
repo_releases_attr_dict = {'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'tag_name': 'Tag',
|
||||
'target_commitish': 'Branch',
|
||||
'assets': 'Assets',
|
||||
'draft': 'Is draft?',
|
||||
'prerelease': 'Is prerelease?',
|
||||
'created_at': 'Created at',
|
||||
'published_at': 'Published at'}
|
||||
|
||||
|
||||
# Profile attributes
|
||||
profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at']
|
||||
# Profile attribute dictionary
|
||||
profile_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'login': 'Username',
|
||||
'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'bio': 'Bio',
|
||||
'blog': 'Blog',
|
||||
'location': 'Location',
|
||||
'followers': 'Followers',
|
||||
'following': 'Following',
|
||||
'twitter_username': 'Twitter Handle',
|
||||
'public_gists': 'Gists (public)',
|
||||
'public_repos': 'Repositories (public)',
|
||||
'company': 'Organization',
|
||||
'hireable': 'Is hireable?',
|
||||
'site_admin': 'Is site admin?',
|
||||
'created_at': 'Joined at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# User attributes
|
||||
user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url']
|
||||
# User attribute dictionary
|
||||
user_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'gravatar_id': 'Gravatar ID',
|
||||
'site_admin': 'Is site admin?',
|
||||
'type': 'Account type',
|
||||
'html_url': 'URL'}
|
||||
|
||||
|
||||
# Topic atrributes
|
||||
topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at']
|
||||
# Topic attribute dictionary
|
||||
topic_attr_dict = {'score': 'Score',
|
||||
'curated': 'Curated',
|
||||
'featured': 'Featured',
|
||||
'display_name': 'Display name',
|
||||
'created_by': 'Created by',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Gists attributes
|
||||
gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at']
|
||||
# Gists attribute dictionary
|
||||
gists_attr_dict = {'node_id': 'Node ID',
|
||||
'description': 'About',
|
||||
'comments': 'Comments',
|
||||
'files': 'Files',
|
||||
'git_push_url': 'Git Push URL',
|
||||
'public': 'Is public?',
|
||||
'truncated': 'Is truncated?',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# Issue attributes
|
||||
issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at']
|
||||
# Issue attribute dict
|
||||
issue_attr_dict = {'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'score': 'Score',
|
||||
'state': 'State',
|
||||
'closed_at': 'Closed at',
|
||||
'number': 'Number',
|
||||
'comments': 'Comments',
|
||||
'milestone': 'Milestone',
|
||||
'assignee': 'Assignee',
|
||||
'assignees': 'Assignees',
|
||||
'labels': 'Labels',
|
||||
'draft': 'Is draft?',
|
||||
'locked': 'Is locked?',
|
||||
'created_at': 'Created at'}
|
||||
|
||||
|
||||
# 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']
|
||||
# Issue attribute dict
|
||||
repo_issues_attr_dict = {'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'number': 'Number',
|
||||
'state': 'State',
|
||||
'reactions': 'Reactions',
|
||||
'comments': 'Comments',
|
||||
'milestone': 'Milestone',
|
||||
'assignee': 'Assignee',
|
||||
'assignees': 'Assignees',
|
||||
'author_association': 'Author association',
|
||||
'labels': 'Labels',
|
||||
'locked': 'Is locked?',
|
||||
'active_lock_reason': 'Lock reason',
|
||||
'closed_at': 'Closed at',
|
||||
'created_at': 'Created at',
|
||||
'updated_at': 'Updated at'}
|
||||
|
||||
|
||||
# User organizations attributes
|
||||
user_orgs_attrs = ['avatar_url','id','node_id','url','description']
|
||||
user_orgs_attr_dict = {'avatar_url': 'Profile Photo',
|
||||
'id': 'ID',
|
||||
'node_id': 'Node ID',
|
||||
'url': 'URL',
|
||||
'description': 'About'}
|
||||
|
||||
|
||||
# Author dictionary
|
||||
author_dict = {'Alias': 'rly0nheart',
|
||||
'Country': 'Zambia, Africa',
|
||||
'About.me': 'https://about.me/rly0nheart',
|
||||
'BuyMeACoffee': 'https://buymeacoffee.com/189381184'}
|
||||
|
||||
|
||||
'''
|
||||
pathFinder()
|
||||
This function is responsible for creating/checking the availability of the (.logs, output, downloads) folders,
|
||||
enabling logging to automatically log network/user activity to a file,
|
||||
and logging the start of a session.
|
||||
'''
|
||||
def pathFinder():
|
||||
'''
|
||||
Windows based machines
|
||||
Here we check the existence of 3 directories
|
||||
If the directories exist, we ignore them.
|
||||
If not, we create them.
|
||||
'''
|
||||
if sys.platform.lower().startswith(('win', 'darwin')):
|
||||
if os.path.exists('.logs'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['mkdir','.logs'])
|
||||
|
||||
if os.path.exists('output'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['mkdir','output'])
|
||||
|
||||
if os.path.exists('downloads'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['mkdir','.downloads'])
|
||||
else:
|
||||
'''
|
||||
Here we do the same as above,
|
||||
except we are not creating on windows based machines
|
||||
'''
|
||||
if os.path.exists('.logs'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['sudo','mkdir','.logs'], shell=False)
|
||||
|
||||
if os.path.exists('output'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['sudo','mkdir','output'], shell=False)
|
||||
|
||||
if os.path.exists('downloads'):
|
||||
pass
|
||||
else:
|
||||
subprocess.run(['sudo', 'mkdir', 'downloads'], shell=False)
|
||||
|
||||
'''
|
||||
Configure logging to log activities to a file, which will be named by the date and time a session was opened.
|
||||
'''
|
||||
now = datetime.now()
|
||||
now_formatted = now.strftime('%Y-%m-%d %H:%M:%S%p')
|
||||
logging.basicConfig(filename=f'.logs/{now_formatted}.log', format='[%(asctime)s] [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S%p', level=logging.DEBUG)
|
||||
# Log the start of a session
|
||||
logging.info(logRoller.sessionOpened.format(platform.node(), getpass.getuser()))
|
||||
|
||||
|
||||
'''
|
||||
onStart()
|
||||
This is the main function, responsible for mapping commands, calling other functions, and catching exceptions
|
||||
'''
|
||||
def onStart():
|
||||
pathFinder()
|
||||
# A list of tuples mapping commands to their functions
|
||||
command_map = [('org', Help.Org),
|
||||
('org:events', orgEvents),
|
||||
('org:profile', orgProfile),
|
||||
('org:repos', orgRepos),
|
||||
('org:member', orgMember),
|
||||
('repo', Help.Repo),
|
||||
('repo:pathcontents', pathContents),
|
||||
('repo:profile', repoProfile),
|
||||
('repo:contributors', repoContributors),
|
||||
('repo:stargazers', repoStargazers),
|
||||
('repo:forks', repoForks),
|
||||
('repo:issues', repoIssues),
|
||||
('repo:releases', repoReleases),
|
||||
('user', Help.User),
|
||||
('user:repos', userRepos),
|
||||
('user:gists', userGists),
|
||||
('user:orgs', userOrgs),
|
||||
('user:profile', userProfile),
|
||||
('user:events', userEvents),
|
||||
('user:followers', userFollowers),
|
||||
('user:follows', userFollows),
|
||||
('user:following', userFollowing),
|
||||
('user:subscriptions', userSubscriptions),
|
||||
('search', Help.Search),
|
||||
('search:users', userSearch),
|
||||
('search:repos', repoSearch),
|
||||
('search:topics', topicSearch),
|
||||
('search:issues', issueSearch),
|
||||
('search:commits', commitsSearch),
|
||||
('source', Help.Source),
|
||||
('source:tarball', downloadTarball),
|
||||
('source:zipball', downloadZipball),
|
||||
('logs', Help.Logs),
|
||||
('logs:view',viewLogs),
|
||||
('logs:read',readLog),
|
||||
('logs:delete',deleteLog),
|
||||
('help', Help.helpCommand),
|
||||
('help:version', Help.versionCommand),
|
||||
('help:source', Help.sourceCommand),
|
||||
('help:search', Help.searchCommand),
|
||||
('help:user', Help.userCommand),
|
||||
('help:repo', Help.repoCommand),
|
||||
('help:logs', Help.logsCommand),
|
||||
('help:org', Help.orgCommand),
|
||||
('author', author),
|
||||
('about', about),
|
||||
('clear',clearScreen),
|
||||
('version', Help.Version),
|
||||
('version:info', versionInfo),
|
||||
('version:check', versionCheck),
|
||||
('exit', exitSession)]
|
||||
|
||||
|
||||
print(Banner.nameLogo)
|
||||
'''
|
||||
Main loop keeps octosuite running, this will break if Octosuite detects a KeyboardInterrupt (Ctrl+C)
|
||||
or if the 'exit' command is entered.
|
||||
'''
|
||||
while True:
|
||||
try:
|
||||
command_input = input(f'''{Color.white}┌──({Color.red}{getpass.getuser()}{Color.white}@{Color.red}octosuite{Color.white})\n├──[{Color.green}~{os.getcwd()}{Color.white}]\n└╼{Color.reset} ''').lower()
|
||||
print('\n')
|
||||
'''
|
||||
Iterating over the command_map and check if the user input matches any command in it [command_map],
|
||||
if there's a match, we return its function. If no match is found, we ignore it.
|
||||
'''
|
||||
for command, function in command_map:
|
||||
if command_input == command:
|
||||
function()
|
||||
print('\n')
|
||||
else:
|
||||
pass
|
||||
|
||||
# This catches the KeyboardInterrupt exception (Ctrl+C)
|
||||
except KeyboardInterrupt:
|
||||
logging.warning(logRoller.Ctrl.format('Ctrl+C'))
|
||||
sys.stdout.write(f"{SignVar.warning} {logRoller.Ctrl}".format(f"{Color.red}Ctrl{Color.reset}+{Color.red}C{Color.reset}")+'\n');break
|
||||
|
||||
# This initially catches all exceptions (except the KeyboardInterrupt)
|
||||
except Exception as e:
|
||||
logging.error(logRoller.Error.format(e))
|
||||
sys.stderr.write(f"{SignVar.error} {logRoller.Error}".format(f'{Color.red}{e}{Color.reset}')+'\n')
|
||||
|
||||
|
||||
# Fetching organization info
|
||||
def orgProfile():
|
||||
organization = input(f'{Color.white}--> @{Color.green}Organization{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/orgs/{organization}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}\n")
|
||||
elif response.status_code == 200:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in org_attrs:
|
||||
print(f'{Color.white}├─ {org_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
csvLogger.logOrgProfile(response)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching user information
|
||||
def userProfile():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/users/{username}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
print(f"\n{Color.white}{response.json()['name']}{Color.reset}")
|
||||
for attr in profile_attrs:
|
||||
print(f'{Color.white}├─ {profile_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}')
|
||||
csvLogger.logUserProfile(response)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching repository information
|
||||
def repoProfile():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.status_code == 200:
|
||||
print(f"\n{Color.white}{response.json()['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f"{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{response.json()[attr]}{Color.reset}")
|
||||
csvLogger.logRepoProfile(response)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Get path contents
|
||||
def pathContents():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
path_name = input(f'{Color.white}--> ~{Color.green}/path/name{Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/contents/{path_name}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.infoNotFound.format(repo_name, username, path_name)}\n")
|
||||
elif response.status_code == 200:
|
||||
for content in response.json():
|
||||
print(f"\n{Color.white}{content['name']}{Color.reset}")
|
||||
for attr in path_attrs:
|
||||
print(f'{Color.white}├─ {path_attr_dict[attr]}: {Color.green}{content[attr]}{Color.reset}')
|
||||
csvLogger.logRepoPathContents(content, repo_name)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# repo contributors
|
||||
def repoContributors():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('contributors'))
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/contributors?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for contributor in response.json():
|
||||
print(f"\n{Color.white}{contributor['login']}{Color.reset}")
|
||||
for attr in user_attrs:
|
||||
print(f'{Color.white}├─ {user_attr_dict[attr]}: {Color.green}{contributor[attr]}{Color.reset}')
|
||||
csvLogger.logRepoContributors(contributor, repo_name)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# repo stargazers
|
||||
def repoStargazers():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repository stargazers'))
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/stargazers?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.json() == {}:
|
||||
sys.stdout.write(f'{SignVar.negative} Repository ({repo_name}) does not have any stargazers.{Color.reset}\n')
|
||||
elif response.status_code == 200:
|
||||
for stargazer in response.json():
|
||||
print(f"\n{Color.white}{stargazer['login']}{Color.reset}")
|
||||
for attr in user_attrs:
|
||||
print(f'{Color.white}├─ {user_attr_dict[attr]}: {Color.green}{stargazer[attr]}{Color.reset}')
|
||||
csvLogger.logRepoStargazers(stargazer, repo_name)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# repo forks
|
||||
def repoForks():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repository forks'))
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/forks?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.json() == {}:
|
||||
sys.stdout.write(f'{SignVar.negative} Repository ({repo_name}) does not have forks.{Color.reset}\n')
|
||||
elif response.status_code == 200:
|
||||
count = 0
|
||||
for fork in response.json():
|
||||
count += 1
|
||||
print(f"\n{Color.white}{fork['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f'{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{fork[attr]}{Color.reset}')
|
||||
csvLogger.logRepoForks(fork, count)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Repo issues
|
||||
def repoIssues():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repository issues'))
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/issues?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.json() == []:
|
||||
sys.stdout.write(f'{SignVar.negative} Repository ({repo_name}) does not have open issues.{Color.reset}\n')
|
||||
elif response.status_code == 200:
|
||||
for issue in response.json():
|
||||
print(f"\n{Color.white}{issue['title']}{Color.reset}")
|
||||
for attr in repo_issues_attrs:
|
||||
print(f'{Color.white}├─ {repo_issues_attr_dict[attr]}: {Color.green}{issue[attr]}{Color.reset}')
|
||||
print(issue['body'])
|
||||
csvLogger.logRepoIssues(issue, repo_name)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Repo releases
|
||||
def repoReleases():
|
||||
repo_name = input(f'{Color.white}--> %{Color.green}Repository{Color.reset} ')
|
||||
username = input(f'{Color.white}--> @{Color.green}Owner{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repository releases'))
|
||||
response = requests.get(f'{endpoint}/repos/{username}/{repo_name}/releases?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.repoOrUserNotFound.format(repo_name, username)}\n")
|
||||
elif response.json() == []:
|
||||
sys.stdout.write(f"{SignVar.negative} Repository ({repo_name}) does not have releases.{Color.reset}\n")
|
||||
elif response.status_code == 200:
|
||||
for release in response.json():
|
||||
print(f"\n{Color.white}{release['name']}{Color.reset}")
|
||||
for attr in repo_releases_attrs:
|
||||
print(f'{Color.white}├─ {repo_releases_attr_dict[attr]}: {Color.green}{release[attr]}{Color.reset}')
|
||||
print(release['body'])
|
||||
csvLogger.logRepoReleases(release, repo_name)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching organization repositories
|
||||
def orgRepos():
|
||||
organization = input(f'{Color.white}--> @{Color.green}Organization{Color.white} (username){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('organization repositories'))
|
||||
response = requests.get(f'{endpoint}/orgs/{organization}/repos?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}\n")
|
||||
elif response.status_code == 200:
|
||||
for repository in response.json():
|
||||
print(f"\n{Color.white}{repository['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f"{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{repository[attr]}{Color.reset}")
|
||||
csvLogger.logOrgRepos(repository, organization)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# organization events
|
||||
def orgEvents():
|
||||
organization = input(f"{Color.white}--> @{Color.green}Organization{Color.white} (username){Color.reset} ")
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('organization events'))
|
||||
response = requests.get(f'{endpoint}/orgs/{organization}/events?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.orgNotFound.format(organization)}\n")
|
||||
elif response.status_code == 200:
|
||||
for event in response.json():
|
||||
print(f"\n{Color.white}{event['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{event['type']}{Color.reset}\n{Color.white}├─ Created at: {Color.green}{event['created_at']}{Color.reset}")
|
||||
pprint(event['payload'])
|
||||
csvLogger.logOrgEvents(event, organization)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# organization member
|
||||
def orgMember():
|
||||
organization = input(f"{Color.white}--> @{Color.green}Organization{Color.white} (username){Color.reset} ")
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/orgs/{organization}/public_members/{username}')
|
||||
if response.status_code == 204:
|
||||
sys.stdout.write(f"{SignVar.positive} User ({username}) is a public member of the organization ({organization}){Color.reset}\n")
|
||||
else:
|
||||
sys.stdout.write(f"{SignVar.negative} {response.json()['message']}{Color.reset}\n")
|
||||
|
||||
|
||||
# Fetching user repositories
|
||||
def userRepos():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repositories'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/repos?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for repository in response.json():
|
||||
print(f"\n{Color.white}{repository['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f"{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{repository[attr]}{Color.reset}")
|
||||
csvLogger.logUserRepos(repository, username)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching user's gists
|
||||
def userGists():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(f'{SignVar.prompt+logRoller.limitInput.format("gists")}')
|
||||
response = requests.get(f'{endpoint}/users/{username}/gists?per_page={limit}')
|
||||
#pprint(response.json())
|
||||
if response.json() == []:
|
||||
sys.stdout.write(f'{SignVar.negative} User does not have gists.{Color.reset}\n')
|
||||
elif response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for gist in response.json():
|
||||
print(f"\n{Color.white}{gist['id']}{Color.reset}")
|
||||
for attr in gists_attrs:
|
||||
print(f"{Color.white}├─ {gists_attr_dict[attr]}: {Color.green}{gist[attr]}{Color.reset}")
|
||||
csvLogger.logUserGists(gist)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
|
||||
# Fetching a list of organizations that a user owns or belongs to
|
||||
def userOrgs():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('user organizations'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/orgs?per_page={limit}')
|
||||
if response.json() == []:
|
||||
sys.stdout.write(f'{SignVar.negative} User ({username}) does not (belong to/own) any organizations.{Color.reset}\n')
|
||||
elif response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for organization in response.json():
|
||||
print(f'\n{Color.white}{organization["login"]}{Color.reset}')
|
||||
for attr in user_orgs_attrs:
|
||||
print(f'{Color.white}├─ {user_orgs_attr_dict[attr]}: {Color.green}{organization[attr]}{Color.reset}')
|
||||
csvLogger.logUserOrgs(organization, username)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching a users events
|
||||
def userEvents():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('events'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/events/public?per_page={limit}')
|
||||
if response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for event in response.json():
|
||||
print(f"\n{Color.white}{event['id']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Actor: {Color.green}{event['actor']['login']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Type: {Color.green}{event['type']}{Color.green}")
|
||||
print(f"{Color.white}├─ Repository: {Color.green}{event['repo']['name']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Created at: {Color.green}{event['created_at']}{Color.reset}")
|
||||
pprint(event['payload'])
|
||||
csvLogger.logUserEvents(event)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching a target user's subscriptions
|
||||
def userSubscriptions():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('user subscriptions'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/subscriptions?per_page={limit}')
|
||||
if response.json() == []:
|
||||
print(f"{SignVar.negative} User does not have any subscriptions.{Color.reset}\n")
|
||||
elif response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for repository in response.json():
|
||||
print(f"\n{Color.white}{repository['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f"{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{repository[attr]}{Color.reset}")
|
||||
csvLogger.logUserSubscriptions(repository, username)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching a list of users the target follows
|
||||
def userFollowing():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('user\' following'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/following?per_page={limit}')
|
||||
if response.json() == []:
|
||||
sys.stdout.write(f'{SignVar.negative} User ({username})does not follow anyone.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for user in response.json():
|
||||
print(f"\n{Color.white}@{user['login']}{Color.reset}")
|
||||
for attr in user_attrs:
|
||||
print(f"{Color.white}├─ {user_attr_dict[attr]}: {Color.green}{user[attr]}{Color.reset}")
|
||||
csvLogger.logUserFollowing(user, username)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Fetching user's followera'
|
||||
def userFollowers():
|
||||
username = input(f'{Color.white}--> @{Color.green}Username{Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('user followers'))
|
||||
response = requests.get(f'{endpoint}/users/{username}/followers?per_page={limit}')
|
||||
if response.json() == []:
|
||||
sys.stdout.write(f'{SignVar.negative} User ({username})does not have followers.{Color.reset}')
|
||||
elif response.status_code == 404:
|
||||
sys.stdout.write(f"{SignVar.negative} {logRoller.userNotFound.format(username)}\n")
|
||||
elif response.status_code == 200:
|
||||
for follower in response.json():
|
||||
print(f"\n{Color.white}@{follower['login']}{Color.reset}")
|
||||
for attr in user_attrs:
|
||||
print(f"{Color.white}├─ {user_attr_dict[attr]}: {Color.green}{follower[attr]}{Color.reset}")
|
||||
csvLogger.logUserFollowers(follower, username)
|
||||
else:
|
||||
pprint(response.json())
|
||||
|
||||
|
||||
# Checking whether or not user[A] follows user[B]
|
||||
def userFollows():
|
||||
user_a = input(f'{Color.white}--> @{Color.green}user{Color.white}(A) (username){Color.reset} ')
|
||||
user_b = input(f'{Color.white}--> @{Color.green}user{Color.white}(B) (username){Color.reset} ')
|
||||
response = requests.get(f'{endpoint}/users/{user_a}/following/{user_b}')
|
||||
if response.status_code == 204:
|
||||
sys.stdout.write(f'{SignVar.positive} @{user_a} FOLLOWS @{user_b}{Color.reset}\n')
|
||||
else:
|
||||
sys.stdout.write(f'{SignVar.negative} @{user_a} DOES NOT FOLLOW @{user_b}{Color.reset}\n')
|
||||
|
||||
|
||||
# User search
|
||||
def userSearch():
|
||||
query = input(f'{Color.white}--> @{Color.green}Query{Color.white} (eg. john){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('user search'))
|
||||
response = requests.get(f'{endpoint}/search/users?q={query}&per_page={limit}').json()
|
||||
for user in response['items']:
|
||||
print(f"\n{Color.white}@{user['login']}{Color.reset}")
|
||||
for attr in user_attrs:
|
||||
print(f"{Color.white}├─ {user_attr_dict[attr]}: {Color.green}{user[attr]}{Color.reset}")
|
||||
csvLogger.logUserSearch(user, query)
|
||||
|
||||
|
||||
# Repository search
|
||||
def repoSearch():
|
||||
query = input(f'{Color.white}--> %{Color.green}Query{Color.white} (eg. git){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('repositor[y][ies] search'))
|
||||
response = requests.get(f'{endpoint}/search/repositories?q={query}&per_page={limit}').json()
|
||||
for repository in response['items']:
|
||||
print(f"\n{Color.white}{repository['full_name']}{Color.reset}")
|
||||
for attr in repo_attrs:
|
||||
print(f"{Color.white}├─ {repo_attr_dict[attr]}: {Color.green}{repository[attr]}{Color.reset}")
|
||||
csvLogger.logRepoSearch(repository, query)
|
||||
|
||||
|
||||
# Topics search
|
||||
def topicSearch():
|
||||
query = input(f'{Color.white}--> #{Color.green}Query{Color.white} (eg. osint){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('topic(s) search'))
|
||||
response = requests.get(f'{endpoint}/search/topics?q={query}&per_page={limit}').json()
|
||||
for topic in response['items']:
|
||||
print(f"\n{Color.white}{topic['name']}{Color.reset}")
|
||||
for attr in topic_attrs:
|
||||
print(f"{Color.white}├─ {topic_attr_dict[attr]}: {Color.green}{topic[attr]}{Color.reset}")
|
||||
csvLogger.logTopicSearch(topic, query)
|
||||
|
||||
|
||||
# Issue search
|
||||
def issueSearch():
|
||||
query = input(f'{Color.white}--> !{Color.green}Query{Color.white} (eg. error){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('issue(s) search'))
|
||||
response = requests.get(f'{endpoint}/search/issues?q={query}&per_page={limit}').json()
|
||||
for issue in response['items']:
|
||||
print(f"\n\n{Color.white}{issue['title']}{Color.reset}")
|
||||
for attr in repo_issues_attrs:
|
||||
print(f"{Color.white}├─ {repo_issues_attr_dict[attr]}: {Color.green}{issue[attr]}{Color.reset}")
|
||||
print(issue['body'])
|
||||
csvLogger.logIssueSearch(issue, query)
|
||||
|
||||
|
||||
# Commits search
|
||||
def commitsSearch():
|
||||
query = input(f'{Color.white}--> :{Color.green}Query{Color.white} (eg. filename:index.php){Color.reset} ')
|
||||
limit = input(SignVar.prompt+logRoller.limitInput.format('commit(s) search'))
|
||||
response = requests.get(f'{endpoint}/search/commits?q={query}&per_page={limit}').json()
|
||||
for commit in response['items']:
|
||||
print(f"\n{Color.white}{commit['commit']['tree']['sha']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Author: {commit['commit']['author']['name']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Username: {Color.green}{commit['author']['login']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Email: {Color.green}{commit['commit']['author']['email']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Commiter: {Color.green}{commit['commit']['committer']['name']}{Color.reset}")
|
||||
print(f"{Color.white}├─ Repository: {Color.green}{commit['repository']['full_name']}{Color.reset}")
|
||||
print(f"{Color.white}├─ URL: {Color.green}{commit['html_url']}{Color.reset}")
|
||||
pprint(commit['commit']['message'])
|
||||
csvLogger.logCommitsSearch(commit, query)
|
||||
|
||||
|
||||
# View octosuite log files
|
||||
def viewLogs():
|
||||
logging.info(logRoller.viewingLogs)
|
||||
logs = os.listdir('.logs')
|
||||
print(f'''{Color.white}
|
||||
Log Size{Color.reset}
|
||||
--- ---------''')
|
||||
for log in logs:
|
||||
print(f"{log}\t ",os.path.getsize(".logs/"+log),"bytes")
|
||||
|
||||
|
||||
# Read a specified log file
|
||||
def readLog():
|
||||
log_file = input(f"{Color.white}--> .log date (eg. 2022-04-27 10:09:36AM){Color.reset} ")
|
||||
with open(f'.logs/{log_file}.log', 'r') as log:
|
||||
logging.info(logRoller.readingLog.format(log_file))
|
||||
print("\n"+log.read())
|
||||
|
||||
|
||||
# Delete a specified log file
|
||||
def deleteLog():
|
||||
log_file = input(f"{Color.white}--> .log date (eg. 2022-04-27 10:09:36AM){Color.reset} ")
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['del',f'.logs\{log_file}.log'])
|
||||
else:
|
||||
subprocess.run(['sudo','rm',f'.logs/{log_file}.log'],shell=False)
|
||||
|
||||
logging.info(logRoller.deletedLog.format(log_file))
|
||||
sys.stdout.write(f"{SignVar.positive} {logRoller.deletedLog.format(log_file)}\n")
|
||||
|
||||
|
||||
# Downloading release tarball
|
||||
def downloadTarball():
|
||||
logging.info(logRoller.fileDownloading.format(f'octosuite.v{Banner.versionTag}.tar'))
|
||||
sys.stdout.write(SignVar.info+' '+logRoller.fileDownloading.format(f'octosuite.v{Banner.versionTag}.tar')+'...\n')
|
||||
data = requests.get(f'{endpoint}/repos/rly0nheart/octosuite/tarball/{Banner.versionTag}')
|
||||
if data.status_code == 404:
|
||||
logging.info(logRoller.tagNotFound.format(Banner.versionTag))
|
||||
sys.stdout.write(f'{SignVar.negative} {logRoller.tagNotFound.format(Banner.versionTag)}\n')
|
||||
else:
|
||||
with open(f'downloads/octosuite.v{Banner.versionTag}.tar', 'wb') as file:
|
||||
file.write(data.content)
|
||||
file.close()
|
||||
|
||||
logging.info(logRoller.fileDownloaded.format(f'octosuite.v{Banner.versionTag}.tar'))
|
||||
sys.stdout.write(SignVar.positive+' '+logRoller.fileDownloaded.format(f'octosuite.v{Banner.versionTag}.tar'))
|
||||
|
||||
|
||||
# Downloading release zipball
|
||||
def downloadZipball():
|
||||
logging.info(logRoller.fileDownloading.format(f'octosuite.v{Banner.versionTag}.zip'))
|
||||
sys.stdout.write(SignVar.info+' '+logRoller.fileDownloading.format(f'octosuite.v{Banner.versionTag}.zip')+'...\n')
|
||||
data = requests.get(f'{endpoint}/repos/rly0nheart/octosuite/zipball/{Banner.versionTag}')
|
||||
if data.status_code == 404:
|
||||
logging.info(logRoller.tagNotFound.format(Banner.versionTag))
|
||||
sys.sdtout.write(f'{SignVar.negative} {logRoller.tagNotFound.format(Banner.versionTag)}\n')
|
||||
else:
|
||||
with open(f'downloads/octosuite.v{Banner.versionTag}.zip', 'wb') as file:
|
||||
file.write(data.content)
|
||||
file.close()
|
||||
|
||||
logging.info(logRoller.fileDownloaded.format(f'octosuite.v{Banner.versionTag}.zip'))
|
||||
sys.stdout.write(SignVar.positive+' '+logRoller.fileDownloaded.format(f'octosuite.v{Banner.versionTag}.zip'))
|
||||
|
||||
|
||||
def versionCheck():
|
||||
response = requests.get(f"{endpoint}/repos/rly0nheart/octosuite/releases/latest")
|
||||
if response.json()['tag_name'] == Banner.versionTag:
|
||||
sys.stdout.write(f"{SignVar.positive} Octosuite is up to date. Check again soon :)\n")
|
||||
else:
|
||||
sys.stdout.write(f"{SignVar.info} A new release is available (octosuite.v{response.json()['tag_name']}). Exit Octosuite and run '{Color.green}pip install --upgrade octosuite{Color.white}' to download and install the update.{Color.reset}\n")
|
||||
|
||||
|
||||
# Author info
|
||||
def author():
|
||||
print(f'{Color.white}Richard Mwewa (Ritchie){Color.reset}')
|
||||
for key,value in author_dict.items():
|
||||
print(f'{Color.white}├─ {key}: {Color.green}{value}{Color.reset}')
|
||||
|
||||
|
||||
def about():
|
||||
sys.stdout.write('''
|
||||
OCTOSUITE © 2022 Richard Mwewa
|
||||
|
||||
An advanced and lightning fast framework for gathering open-source intelligence on GitHub users and organizations.
|
||||
|
||||
Read the wiki: https://github.com/rly0nheart/octosuite/wiki
|
||||
GitHub REST API documentation: https://docs.github.com/rest
|
||||
''')
|
||||
|
||||
|
||||
# Close session
|
||||
def exitSession():
|
||||
prompt = input(f'{SignVar.prompt} This will close the current session, continue? (Y/n) ').lower()
|
||||
if prompt == 'y':
|
||||
logging.info(logRoller.sessionClosed.format('exit'))
|
||||
sys.stdout.write(f"{SignVar.info} {logRoller.sessionClosed.format(datetime.now())}\n");exit()
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
# Clear screen
|
||||
def clearScreen():
|
||||
'''
|
||||
We use 'cls' on Windows machines to clear the screen,
|
||||
otherwise, we use 'clear'
|
||||
'''
|
||||
if sys.platform.lower().startswith(('win','darwin')):
|
||||
subprocess.run(['cls'])
|
||||
else:
|
||||
subprocess.run(['clear'],shell=False)
|
||||
|
||||
|
||||
# Show version information
|
||||
def versionInfo():
|
||||
'''
|
||||
Yes... the changelog is hard coded
|
||||
It's actually frustrating having to change this everytime I publish a new release lol
|
||||
'''
|
||||
sys.stdout.write(f'''
|
||||
OCTOSUITE.v{Banner.versionTag}
|
||||
|
||||
What's changed?
|
||||
{'='*15}
|
||||
[fix] error in source commands (source:tarball, source:zipball)
|
||||
''')
|
||||
15
octosuite/sign_vars.py
Normal file
15
octosuite/sign_vars.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from octosuite.colors import Color
|
||||
|
||||
'''
|
||||
Attributes
|
||||
*Even here, I couldn't think of a good name.*
|
||||
The Attributes class holds the signs/symbols that show what a notification in OctoSuite might be all about.
|
||||
This might not be very important or necessary in some cases, but I think it's better to know the severerity of the notifications you get in a program.
|
||||
'''
|
||||
class SignVar:
|
||||
prompt = f'{Color.white}[{Color.green} ? {Color.white}]{Color.reset}'
|
||||
warning = f'{Color.white}[{Color.red} ! {Color.white}]{Color.reset}'
|
||||
error = f'{Color.white}[{Color.red} x {Color.white}]{Color.reset}'
|
||||
positive = f'{Color.white}[{Color.green} + {Color.white}]{Color.reset}'
|
||||
negative = f'{Color.white}[{Color.red} - {Color.white}]{Color.reset}'
|
||||
info = f'{Color.white}[{Color.green} * {Color.white}]{Color.reset}'
|
||||
@@ -1,2 +0,0 @@
|
||||
tqdm
|
||||
requests
|
||||
31
setup.py
Normal file
31
setup.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import setuptools
|
||||
|
||||
with open("README.md", "r", encoding="utf-8") as file:
|
||||
long_description = file.read()
|
||||
|
||||
setuptools.setup(
|
||||
name="octosuite",
|
||||
version="2.1.1",
|
||||
author="Richard Mwewa",
|
||||
author_email="richardmwewa@duck.com",
|
||||
packages=["octosuite"],
|
||||
description="Advanced Github OSINT Framework",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/rly0nheart/octosuite",
|
||||
license="GNU General Public License v3 (GPLv3)",
|
||||
install_requires=["requests"],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Information Technology',
|
||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||
'Operating System :: OS Independent',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 3'
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"octosuite=octosuite.main:onStart",
|
||||
]
|
||||
},
|
||||
)
|
||||
@@ -1,118 +0,0 @@
|
||||
from utilities.colors import Color
|
||||
|
||||
class Help:
|
||||
usageText = 'Use {} to get started'
|
||||
def updateCommand():
|
||||
print(f'''
|
||||
{Color.white}Update subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('update:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
check Check for updates
|
||||
install Download and install updates''')
|
||||
|
||||
|
||||
def searchCommand():
|
||||
print(f'''
|
||||
{Color.white}Search subcommands{Color.reset}
|
||||
{'='*18}
|
||||
{Help.usageText.format('search:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
users Search user(s)
|
||||
repos Search repositor[yies]
|
||||
topics Search topic(s)
|
||||
issues Search issue(s)
|
||||
commits Search commit(s)''')
|
||||
|
||||
|
||||
def userCommand():
|
||||
print(f'''
|
||||
{Color.white}User subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('user:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get a user's profile info
|
||||
gists Return a users's gists
|
||||
orgs Return organizations that a user belongs to/owns
|
||||
repos Return a user's repositories
|
||||
events Return a user's events
|
||||
followers Return a user's followers
|
||||
following Check if user[A] follows user[B]
|
||||
subscriptions Return a user's subscriptions''')
|
||||
|
||||
|
||||
def orgCommand():
|
||||
print(f'''
|
||||
{Color.white}Org subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('org:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get an organization's info
|
||||
repos Return an organization's repositories
|
||||
events Return an organization's events
|
||||
member Check if a specified user is a public member of the target organization''')
|
||||
|
||||
|
||||
def repoCommand():
|
||||
print(f'''
|
||||
{Color.white}Repo subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('repo:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
profile Get a repository's info
|
||||
forks Return a repository's forks
|
||||
releases Return a repository's releases
|
||||
languages Return a repository's languages
|
||||
stargazers Return a repository's stargazers
|
||||
pathcontents List contents in a path of a repository''')
|
||||
|
||||
|
||||
def logsCommand():
|
||||
print(f'''
|
||||
{Color.white}Logs subcommands{Color.reset}
|
||||
{'='*17}
|
||||
{Help.usageText.format('logs:<subcommand>')}
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
view View logs
|
||||
read Read log
|
||||
delete Delete log''')
|
||||
|
||||
|
||||
def helpCommand():
|
||||
print(f'''
|
||||
{Color.white}Core commands{Color.reset}
|
||||
{'='*13}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
help Help menu
|
||||
exit Close session
|
||||
clear Clear screen
|
||||
about Program's info
|
||||
author Developer's info
|
||||
version Version info
|
||||
|
||||
|
||||
{Color.white}Help subcommands{Color.reset}
|
||||
{'='*16}
|
||||
{Help.usageText.format('help:<subcommand>')}
|
||||
|
||||
{Color.white}Command Description{Color.reset}
|
||||
------- -----------
|
||||
logs List all logs management commands
|
||||
repo List all repository investigation commands
|
||||
user List all users investigation commands
|
||||
search List all target discovery commands
|
||||
update List all program updates managememt commands''')
|
||||
@@ -1,20 +0,0 @@
|
||||
import getpass
|
||||
from utilities.colors import Color
|
||||
|
||||
class Banner:
|
||||
versionTag = '2.0.1-alpha'
|
||||
nameLogo = f'''{Color.white}
|
||||
_______ __ _______ __ __
|
||||
| |.----.| |_.-----.| __|.--.--.|__| |_.-----.
|
||||
| - || __|| _| _ ||__ || | || | _| -__|
|
||||
|_______||____||____|_____||_______||_____||__|____|_____|
|
||||
v{versionTag}
|
||||
{Color.white}— Advanced Github {Color.red}OSINT{Color.white} Framework{Color.reset}
|
||||
|
||||
|
||||
.:{Color.white}{Color.green}{getpass.getuser()}{Color.reset}:.
|
||||
|
||||
- {Color.white}Use {Color.green}help{Color.reset}{Color.white} command for usage{Color.reset}
|
||||
- {Color.white}Commands are case insensitive{Color.reset}
|
||||
{'-'*32}
|
||||
'''
|
||||
Reference in New Issue
Block a user