Compare commits

...

26 Commits

Author SHA1 Message Date
Richard Mwewa
9ef14f3e4f Update octosuite 2022-04-14 11:19:34 +02:00
Richard Mwewa
23270e8da9 Update octosuite 2022-04-14 11:15:18 +02:00
Richard Mwewa
619070348b Update banner.py 2022-04-14 11:13:01 +02:00
Richard Mwewa
5718c5f229 Update colors.py 2022-04-14 11:11:41 +02:00
Richard Mwewa
b4f009c7d4 Update main.py 2022-04-14 11:09:43 +02:00
Richard Mwewa
82a95d84b0 Update README.md 2022-04-14 11:07:42 +02:00
Richard Mwewa
425525bf94 Update README.md 2022-04-14 00:57:55 +02:00
Richard Mwewa
24c6b54d19 Update main.py 2022-04-11 13:13:57 +02:00
Richard Mwewa
6d1e163d9a Update LICENSE 2022-04-11 13:03:07 +02:00
Richard Mwewa
a8730864e9 Update README.md 2022-04-11 13:01:03 +02:00
Richard Mwewa
315cb7ec6f Update colors.py 2022-04-11 12:59:10 +02:00
Richard Mwewa
2afac6c0d4 Update banner.py 2022-04-11 12:58:23 +02:00
Richard Mwewa
b226f1b53b Update main.py 2022-04-11 12:56:19 +02:00
Richard Mwewa
bb38d95e57 Update README.md 2022-04-09 21:18:56 +02:00
Richard Mwewa
eae41866ec Create config.yml 2022-04-09 20:57:07 +02:00
Richard Mwewa
1e71c2d3bd Create feature_request.md 2022-04-09 20:42:46 +02:00
Richard Mwewa
d91f88d31b Create bug_report.md 2022-04-09 20:40:44 +02:00
Richard Mwewa
d5119a87b0 Update banner.py 2022-04-01 02:26:53 +02:00
Richard Mwewa
c4bd431620 Update main.py 2022-04-01 02:26:06 +02:00
Richard Mwewa
fe16b23fc7 Update banner.py 2022-03-31 12:54:23 +02:00
Richard Mwewa
8156af38d5 Update main.py 2022-03-31 12:53:29 +02:00
Richard Mwewa
6fb6a0ec98 Update main.py 2022-03-28 13:33:54 +02:00
Richard Mwewa
dc2a22aae0 Update dependabot.yml 2022-03-28 13:22:03 +02:00
Richard Mwewa
a2aac88da3 Create dependabot.yml 2022-03-28 13:09:04 +02:00
Richard Mwewa
3dbfc0f18e Update README.md 2022-03-28 12:52:56 +02:00
Richard Mwewa
392b3022d7 Update main.py 2022-03-27 23:01:12 +02:00
10 changed files with 396 additions and 205 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Richard Mwewa (Telegram)
url: https://t.me/rly0nheart/
about: Please ask and answer questions here.
- name: Richard Mwewa (Twitter)
url: https://m.twitter.com/rly0nheart/
about: Please report security vulnerabilities or bugs here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

13
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Enable version updates for pip
- package-ecosystem: "pip"
directory: "/" # Location of package manifests
# Check the tqdm registry for updates every day (weekdays)
schedule:
interval: "daily"

View File

@@ -1,3 +1,4 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 29 June 2007
@@ -631,8 +632,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> octosuite Advanced Github OSINT Framework
Copyright (C) <year> <name of author> Copyright (C) 2022 Richard Mwewa
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

111
README.md
View File

@@ -1,72 +1,126 @@
![Screenshot_2022-03-17_10-12-53](https://user-images.githubusercontent.com/74001397/158868105-b5aba7e8-7342-4268-bd7a-6d6ae0bdae5a.png) ![Screenshot_2022-03-17_10-12-53](https://user-images.githubusercontent.com/74001397/158868105-b5aba7e8-7342-4268-bd7a-6d6ae0bdae5a.png)
![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/rly0nheart/octosuite?style=for-the-badge&logo=github)
![OS](https://img.shields.io/badge/OS-GNU%2FLinux-red?style=for-the-badge&logo=Linux) ![OS](https://img.shields.io/badge/OS-GNU%2FLinux-red?style=for-the-badge&logo=Linux)
![OS](https://img.shields.io/badge/OS-Windows-blue?style=for-the-badge&logo=Windows) ![OS](https://img.shields.io/badge/OS-Windows-blue?style=for-the-badge&logo=Windows)
![OS](https://img.shields.io/badge/OS-Mac-blue?style=for-the-badge&logo=apple) ![OS](https://img.shields.io/badge/OS-Mac-white?style=for-the-badge&logo=apple)
![GitHub](https://img.shields.io/github/license/rly0nheart/octosuite?style=for-the-badge&logo=github) ![GitHub](https://img.shields.io/github/license/rly0nheart/octosuite?style=for-the-badge&logo=github)
![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/rly0nheart/octosuite?style=for-the-badge&logo=github)
![GitHub commits since latest release (by date)](https://img.shields.io/github/commits-since/rly0nheart/octosuite/1.5.2-alpha?style=for-the-badge&logo=github)
![GitHub last commit](https://img.shields.io/github/last-commit/rly0nheart/octosuite?style=for-the-badge&logo=github)
![GitHub repo size](https://img.shields.io/github/repo-size/rly0nheart/octosuite?style=for-the-badge&logo=github) ![GitHub repo size](https://img.shields.io/github/repo-size/rly0nheart/octosuite?style=for-the-badge&logo=github)
![PyPI - Downloads](https://img.shields.io/pypi/dw/octosuite?style=for-the-badge&logo=pypi)
> *Simply gather OSINT on Github users and organizations like a god🔥* > *Simply gather OSINT on Github users & organizations like a God🔥*
# FEATURES
- [x] Fetches organization info
- [x] Fetches user info
- [x] Fetches repository info
- [x] Returns contents of a path from a repository
- [x] Returns a list of repos owned by an organization
- [x] Returns a list of repos owned by a user
- [x] Returns a list of gists owned by a user
- [x] Returns a list of a user's followers
- [x] Checks whether user A follows user B
- [x] Searches users
- [x] Searches repositories
- [x] Searches topics
- [x] Searches issues
- [x] Searches commits
- [x] Easily updates with the 'update' command
- [x] Automatically logs network activity (.logs folder)
# INSTALLATION # INSTALLATION
**clone project**: ## Clone from Github
``` ```
git clone https://github.com/rly0nheart/octosuite.git git clone https://github.com/rly0nheart/octosuite.git
``` ```
![Screenshot_20220414-002508](https://user-images.githubusercontent.com/74001397/163280996-ed0f8817-c3e3-49d8-9e15-93452cb08a3e.jpg)
``` ```
cd octosuite cd octosuite
``` ```
![Screenshot_20220414-002214](https://user-images.githubusercontent.com/74001397/163281317-158bbf7b-073f-457e-8a8a-730d4c0ed413.jpg)
``` ```
pip install -r requirements.txt pip install -r requirements.txt
``` ```
![Screenshot_20220414-004420](https://user-images.githubusercontent.com/74001397/163282481-6cb5efe8-6e5a-4c2b-a8b5-7ff99f7ca293.jpg)
# USAGE
**Linux**:
## Install from PyPI
```
pip install octosuite
```
![Screenshot_20220414-005400](https://user-images.githubusercontent.com/74001397/163283184-e9458439-8074-4338-938b-4588390bb6b7.jpg)
# GITHUB FORK USAGE
## Linux
``` ```
sudo chmod +x octosuite sudo chmod +x octosuite
``` ```
![Screenshot_20220414-004443](https://user-images.githubusercontent.com/74001397/163282620-a5307969-bcce-49d1-ad3c-c3ea0f78fb44.jpg)
``` ```
sudo ./octosuite sudo ./octosuite
``` ```
![Screenshot_20220414-004507](https://user-images.githubusercontent.com/74001397/163282716-41ace7fc-ee04-4c95-985e-68dd3286682c.jpg)
**Windows**:
## Windows
``` ```
python3 octosuite python3 octosuite
``` ```
**Mac**: ## Mac
``` ```
python3 octosuite python3 octosuite
``` ```
# PYPI PACKAGE USAGE
## Linux
```
octosuite
```
## Windows
```
octosuite
```
## Mac
```
octosuite
```
# AVAILABLE COMMANDS # AVAILABLE COMMANDS
| Command | Usage| | COMMAND | DESCRIPTION|
| ------------- |:---------:| | ------------- |:---------:|
| ``orginfo`` | *get organization info* | | ``info:org`` | *get organization info* |
| ``userinfo`` | *get user profile info* | | ``info:user`` | *get user profile info* |
| ``repoinfo`` | *get repository info* | | ``info:repo`` | *get repository info* |
| ``pathcontents`` | *get contents of a path from a specified repository* | | ``info:dev`` | *show developer's info* |
| ``orgrepos`` | *get a list of repositories owned by a specified organization* | | ``path:contents`` | *get contents of a path from a specified repository* |
| ``userrepos`` | *get a list of repositories owned by a specified user* | | ``repos:org`` | *get a list of repositories owned by a specified organization* |
| ``usergists`` | *get a list of gists owned by a specified user* | | ``repos:user`` | *get a list of repositories owned by a specified user* |
| ``userfollowers`` | *get a list of a user's followers* | | ``user:gists`` | *get a list of gists owned by a specified user* |
| ``userfollowing`` | *check whether user A follows user B* | | ``user:followers`` | *get a list of a user's followers* |
| ``usersearch`` | *search user(s)* | | ``user:following`` | *check whether user A follows user B* |
| ``reposearch`` | *search repositor(y)(ies)* | | ``search:users`` | *search user(s)* |
| ``topicsearch`` | *search topics(s)* | | ``search:repos`` | *search repositor(y)(ies)* |
| ``issuesearch`` | *search issue(s)* | | ``search:topics`` | *search topics(s)* |
| ``commitsearch`` | *search commit(s)* | | ``search:issues`` | *search issue(s)* |
| ``usersearch`` | *search user(s)* | | ``search:commits`` | *search commit(s)* |
| ``update`` | *update octosuite* | | ``update`` | *update octosuite* |
| ``changelog`` | *show changelog* | | ``changelog`` | *show changelog* |
| ``author`` | *show author info* |
| ``help`` | *show usage/help* | | ``help`` | *show usage/help* |
| ``exit`` | *exit session* | | ``exit`` | *exit session* |
@@ -75,8 +129,11 @@ python3 octosuite
* *octosuite automatically logs network and minor user activity. The logs are saved by date and time in .logs folder* * *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*** * *Although octosuite was developed to work on **Mac**, **Windows**, or any **Linux** *Distribution*, it has only been tested on **Termux** *and* **Kali Linux***
# PYPI
[PyPI](https://pypi.org/project/octosuite)
# LICENSE # LICENSE
![license](https://user-images.githubusercontent.com/74001397/137917929-2f2cdb0c-4d1d-4e4b-9f0d-e01589e027b5.png) ![license](https://user-images.githubusercontent.com/74001397/137917929-2f2cdb0c-4d1d-4e4b-9f0d-e01589e027b5.png)
# ABOUT AUTHOR # ABOUT DEVELOPER
[About.me](https://about.me/rly0nheart) [About.me](https://about.me/rly0nheart)

View File

@@ -1,4 +1,5 @@
from lib.colors import red,white,green,reset import os
from lib.colors import red,white,white_bg,red_bg,reset
banner = f'''{red} banner = f'''{red}
▒█████ ▄████▄ ▄▄▄█████▓ ▒█████ ██████ █ ██ ██▓▄▄▄█████▓▓█████ ▒█████ ▄████▄ ▄▄▄█████▓ ▒█████ ██████ █ ██ ██▓▄▄▄█████▓▓█████
▒██▒ ██▒▒██▀ ▀█ ▓ ██▒ ▓▒▒██▒ ██▒▒██ ▒ ██ ▓██▒▓██▒▓ ██▒ ▓▒▓█ ▀ ▒██▒ ██▒▒██▀ ▀█ ▓ ██▒ ▓▒▒██▒ ██▒▒██ ▒ ██ ▓██▒▓██▒▓ ██▒ ▓▒▓█ ▀
@@ -7,13 +8,16 @@ banner = f'''{red}
░ ████▓▒░▒ ▓███▀ ░ ▒██▒ ░ ░ ████▓▒░▒██████▒▒▒█████▓ ░██░ ▒██▒ ░ ░▒████▒ ░ ████▓▒░▒ ▓███▀ ░ ▒██▒ ░ ░ ████▓▒░▒██████▒▒▒█████▓ ░██░ ▒██▒ ░ ░▒████▒
░ ▒░▒░▒░ ░ ░▒ ▒ ░ ▒ ░░ ░ ▒░▒░▒░ ▒ ▒▓▒ ▒ ░░▒▓▒ ▒ ▒ ░▓ ▒ ░░ ░░ ▒░ ░ ░ ▒░▒░▒░ ░ ░▒ ▒ ░ ▒ ░░ ░ ▒░▒░▒░ ▒ ▒▓▒ ▒ ░░▒▓▒ ▒ ▒ ░▓ ▒ ░░ ░░ ▒░ ░
░ ▒ ▒░ ░ ▒ ░ ░ ▒ ▒░ ░ ░▒ ░ ░░░▒░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ▒ ▒░ ░ ▒ ░ ░ ▒ ▒░ ░ ░▒ ░ ░░░▒░ ░ ░ ▒ ░ ░ ░ ░ ░
░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░░░ ░ ░ ▒ v1.3.0 ░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░░░ ░ ░ ▒ {red_bg}v1.5.2-alpha{reset}{red}
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
{white}— Advanced Github {red}OSINT{white} Framework{reset} {white}— Advanced Github {red}OSINT{white} Framework{reset}
> {white}use '{green}help{white}' command for usage{reset}
> {white}commands are case sensitive > {white}Current user: {white_bg}{os.getlogin()}{reset}
> {white}Use {white_bg}help{reset}{white} command for usage{reset}
> {white}Commands are {white_bg}case sensitive{reset}
{'-'*30} {'-'*30}
{reset}
''' '''

View File

@@ -1,16 +1,28 @@
import os
import sys import sys
# Colors will be unavailable on non-linux machines
colors = True colors = True
machine = sys.platform machine = sys.platform
if machine.lower().startswith(("os", "win", "darwin")): # Colors will be unavailable on non-linux machines
if machine.lower().startswith(("os", "win", "darwin","ios")):
colors = False colors = False
if not colors: if not colors:
reset = red = white = green = "" reset = red = white = green = green_bg = white_bg = red_bg = ""
else: else:
white = "\033[97m" try:
red = "\033[91m" color_chooser = input(f"[ ? ] Welcome {os.getlogin()}, would you like to enable colors for this session? [Y/n] ")
reset = "\033[0m" if color_chooser.lower() == "y":
green = "\033[92m" white = "\033[97m"
white_bg = "\033[47;30m"
red = "\033[91m"
reset = "\033[0m"
green = "\033[92m"
green_bg = "\033[42;37m"
red_bg = "\033[41;37m"
else:
red = white = green = green_bg = white_bg = red_bg = reset = ""
except KeyboardInterrupt:
exit(f"[ ! ] Process interrupted with Ctrl+C")

View File

@@ -2,19 +2,16 @@
import logging import logging
from src.main import * from src.main import *
from lib.colors import red,white,green,reset from lib.colors import red,red_bg,white,reset
if __name__ == '__main__': if __name__ == '__main__':
while True: try:
try: octosuite().on_start()
octosuite().on_start()
except KeyboardInterrupt:
except KeyboardInterrupt: logging.warning('Process interrupted with Ctrl+C')
logging.info('Process interrupted with Ctrl+C') exit(f'\n{white}[{red} x {white}] Process interrupted with {red_bg}Ctrl+C{reset}')
exit(f'\n{white}[{red}x{white}] Process interrupted with {red}Ctrl{white}+{red}C{reset}')
except Exception as e:
logging.error(f'Session terminated on error: {e}')
except Exception as e: exit(f'\n{white}[{red} ! {white}] Session {red_bg}terminated{reset}{white} on error: {red}{e}{reset}')
logging.error(f'An error occured: {e}')
print(f'{white}[{red}!{white}] An error occured: {red}{e}{reset}')
input(f'\n{white}^ Press any key to continue{reset} ')

View File

@@ -1,16 +1,53 @@
'''
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 os
import logging import logging
import requests import requests
import platform import platform
import subprocess import subprocess
import urllib.request
from tqdm import tqdm from tqdm import tqdm
from pprint import pprint from pprint import pprint
from lib import colors,banner from lib.banner import banner
from datetime import datetime from datetime import datetime
from lib.colors import red, white, green, green_bg, white_bg, red_bg, reset
class octosuite: class octosuite:
def __init__(self): def __init__(self):
# A list of tuples, mapping commands to their respective functionalities
self.commands_base = [('info:org', self.org_info),
('info:user', self.user_profile),
('info:repo', self.repo_info),
('path:contents', self.path_contents),
('repos:org', self.org_repos),
('repos:user', self.user_repos),
('user:gists', self.user_gists),
('user:followers', self.followers),
('user:following', self.following),
('search:users', self.user_search),
('search:repos', self.repo_search),
('search:topics', self.topic_search),
('search:issues', self.issue_search),
('search:commits', self.commits_search),
('update', self.update),
('changelog', self.changelog),
('info:dev', self.author),
('help', self.help),
('exit', self.exit_session)]
# Path attribute # Path attribute
self.path_attrs =['size','type','path','sha','html_url'] self.path_attrs =['size','type','path','sha','html_url']
# Path attribute dictionary # Path attribute dictionary
@@ -20,6 +57,7 @@ class octosuite:
'sha': 'SHA', 'sha': 'SHA',
'html_url': 'URL'} 'html_url': 'URL'}
# Organization attributes # Organization attributes
self.org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at'] self.org_attrs = ['avatar_url','login','id','node_id','email','description','blog','location','followers','following','twitter_username','public_gists','public_repos','type','is_verified','has_organization_projects','has_repository_projects','created_at','updated_at']
# Organization attribute dictionary # Organization attribute dictionary
@@ -42,6 +80,8 @@ class octosuite:
'has_repository_projects': 'Has repository projects?', 'has_repository_projects': 'Has repository projects?',
'created_at': 'Created at', 'created_at': 'Created at',
'updated_at': 'Updated at'} 'updated_at': 'Updated at'}
# Repository attributes # Repository attributes
self.repo_attrs = ['id','description','forks','allow_forking','fork','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at'] self.repo_attrs = ['id','description','forks','allow_forking','fork','stargazers_count','watchers','license','default_branch','visibility','language','open_issues','topics','homepage','clone_url','ssh_url','private','archived','has_downloads','has_issues','has_pages','has_projects','has_wiki','pushed_at','created_at','updated_at']
# Repository attribute dictionary # Repository attribute dictionary
@@ -73,6 +113,7 @@ class octosuite:
'created_at': 'Created at', 'created_at': 'Created at',
'updated_at': 'Updated at'} 'updated_at': 'Updated at'}
# Profile attributes # Profile attributes
self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at'] self.profile_attrs = ['avatar_url','login','id','node_id','bio','blog','location','followers','following','twitter_username','public_gists','public_repos','company','hireable','site_admin','created_at','updated_at']
# Profile attribute dictionary # Profile attribute dictionary
@@ -94,6 +135,7 @@ class octosuite:
'created_at': 'Joined at', 'created_at': 'Joined at',
'updated_at': 'Updated at'} 'updated_at': 'Updated at'}
# User attributes # User attributes
self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url'] self.user_attrs = ['avatar_url','id','node_id','gravatar_id','site_admin','type','html_url']
# User attribute dictionary # User attribute dictionary
@@ -104,6 +146,7 @@ class octosuite:
'site_admin': 'Is site admin?', 'site_admin': 'Is site admin?',
'type': 'Account type', 'type': 'Account type',
'html_url': 'URL'} 'html_url': 'URL'}
# Topic atrributes # Topic atrributes
self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at'] self.topic_attrs = ['score','curated','featured','display_name','created_by','created_at','updated_at']
@@ -115,6 +158,7 @@ class octosuite:
'created_by': 'Created by', 'created_by': 'Created by',
'created_at': 'Created at', 'created_at': 'Created at',
'updated_at': 'Updated at'} 'updated_at': 'Updated at'}
# Gists attributes # Gists attributes
self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at'] self.gists_attrs = ['node_id','description','comments','files','git_push_url','public','truncated','updated_at']
@@ -128,6 +172,7 @@ class octosuite:
'truncated': 'Is truncated?', 'truncated': 'Is truncated?',
'updated_at': 'Updated at'} 'updated_at': 'Updated at'}
# Issue attributes # Issue attributes
self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body'] self.issue_attrs = ['id','node_id','score','state','number','comments','milestone','assignee','assignees','labels','locked','draft','closed_at','body']
# Issue attribute dict # Issue attribute dict
@@ -147,322 +192,318 @@ class octosuite:
'created_at': 'Created at', 'created_at': 'Created at',
'body': 'Body'} 'body': 'Body'}
# Author dictionary # Author dictionary
self.author_dict = {'Alias': 'rly0nheart', self.author_dict = {'Alias': 'rly0nheart',
'Country': 'Zambia, Africa', 'Country': 'Zambia, Africa',
'About.me': 'https://about.me/rly0nheart'} 'About.me': 'https://about.me/rly0nheart'}
def on_start(self): def on_start(self):
logging.info(f'Started new session on {platform.node()}') # Start new session
logging.info(f'Started new session on {platform.node()}:{os.getlogin()}')
# Use 'cls' to clear screen on Windows based machines
# Otherwise, use 'clear'
while True: while True:
if platform.system() == 'Windows': if platform.system().lower().startswith(('win','darwin')):
subprocess.run(['cls']) subprocess.run(['cls'])
else: else:
subprocess.run(['clear'],shell=False) subprocess.run(['clear'],shell=False)
print(banner.banner) print(banner)
command = input(f'''{colors.white}┌─({colors.red}{platform.node()}{colors.white}@{colors.red}octosuite{colors.white})-[{colors.green}{os.getcwd()}{colors.white}]\n└─╼[{colors.green}:~{colors.white}]{colors.reset} ''') command_input = input(f'''{white}┌───({red}{os.getlogin()}{white}@{red}octosuite{white})-[{green}{os.getcwd()}{white}]\n└─╼[{green}:~{white}]{reset} ''')
if command == 'orginfo': # Looping through the commands base to check if the user input command matches any command in the commands base, and return its functionality
self.org_info() # If no match is found, we ignore it
elif command == 'userinfo': for command, functionality in self.commands_base:
self.user_profile() if command == command_input:
elif command == 'repoinfo': functionality()
self.repo_info() else:
elif command == 'pathcontents': pass
self.path_contents()
elif command == 'orgrepos': input(f'\n{white}[{green} ? {white}] Press {white_bg}any key{reset}{white} to continue{reset} ')
self.org_repos()
elif command == 'userrepos':
self.user_repos()
elif command == 'usergists':
self.user_gists()
elif command == 'userfollowers':
self.followers()
elif command == 'userfollowing':
self.following()
elif command == 'usersearch':
self.user_search()
elif command == 'reposearch':
self.repo_search()
elif command == 'topicsearch':
self.topic_search()
elif command == 'issuesearch':
self.issue_search()
elif command == 'commitsearch':
self.commits_search()
elif command == 'update':
self.update()
elif command == 'changelog':
print(self.changelog())
elif command == 'author':
self.author()
elif command == 'help':
print(self.help())
elif command == 'exit':
logging.info('Session terminated.')
exit(f'\n{colors.white}[{colors.red}-{colors.white}] Session terminated.{colors.reset}')
else:
print(f'\n{colors.white}[{colors.red}!{colors.white}] Unknown command: {command}{colors.reset}')
logging.warning(f'Unknown command: {command}')
input(f'\n{colors.white}^ Press any key to continue{colors.reset} ')
def org_info(self): def org_info(self):
organization = input(f'{colors.white}@{colors.green}Organization{colors.white} >> {colors.reset}') organization = input(f'\n{white}[{white_bg}@Organization{reset}{white}] (username){reset} ')
api = f'https://api.github.com/orgs/{organization}' api = f'https://api.github.com/orgs/{organization}'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] Organization @{organization} {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red}-{white}] Organization @{organization} {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
print(f"\n{colors.white}{response['name']}{colors.reset}") print(f"\n{white}{response['name']}{reset}")
for attr in self.org_attrs: for attr in self.org_attrs:
print(f'{colors.white}├─ {self.org_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}') print(f'{white}├─ {self.org_attr_dict[attr]}: {green}{response[attr]}{reset}')
# Fetching user information # Fetching user information
def user_profile(self): def user_profile(self):
username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ')
api = f'https://api.github.com/users/{username}' api = f'https://api.github.com/users/{username}'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] User @{username} {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
print(f"\n{colors.white}{response['name']}{colors.reset}") print(f"\n{white}{response['name']}{reset}")
for attr in self.profile_attrs: for attr in self.profile_attrs:
print(f'{colors.white}├─ {self.profile_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}') print(f'{white}├─ {self.profile_attr_dict[attr]}: {green}{response[attr]}{reset}')
# Fetching repository information # Fetching repository information
def repo_info(self): def repo_info(self):
username = input(f'{colors.white}@{colors.green}Owner-username{colors.white} >> {colors.reset}') repo_name = input(f'\n{white}[{white_bg}%reponame{reset}{white}]{reset} ')
repo_name = input(f'{colors.white}%{colors.green}reponame{colors.white} >> {colors.reset}') username = input(f'{white}[{white_bg}@Owner{reset}{white}] (username){reset} ')
api = f'https://api.github.com/repos/{username}/{repo_name}' api = f'https://api.github.com/repos/{username}/{repo_name}'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] Repository %{repo_name} {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red} - {white}] Repository %{repo_name} or user @{username} {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
print(f"\n{colors.white}{response['full_name']}{colors.reset}") print(f"\n{white}{response['full_name']}{reset}")
for attr in self.repo_attrs: for attr in self.repo_attrs:
print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{response[attr]}{colors.reset}") print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{response[attr]}{reset}")
# Get path contents # Get path contents
def path_contents(self): def path_contents(self):
username = input(f'{colors.white}@{colors.green}Owner-username{colors.white} >> {colors.reset}') username = input(f'\n{white}[{white_bg}@Owner{reset}{white}] (username){reset} ')
repo_name = input(f'{colors.white}%{colors.green}reponame{colors.white} >> {colors.reset}') repo_name = input(f'{white}[{white_bg}%reponame{reset}{white}]{reset} ')
path_name = input(f'{colors.white}/path/name >>{colors.reset} ') path_name = input(f'{white}[{white_bg}/path/name{reset}{white}]{reset} ')
api = f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}' api = f'https://api.github.com/repos/{username}/{repo_name}/contents/{path_name}'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] Information {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red} - {white}] Information {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
for item in response: for item in response:
print(f"\n{colors.white}{item['name']}{colors.reset}") print(f"\n{white}{item['name']}{reset}")
for attr in self.path_attrs: for attr in self.path_attrs:
print(f'{colors.white}├─ {self.path_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}') print(f'{white}├─ {self.path_attr_dict[attr]}: {green}{item[attr]}{reset}')
# Fetching organozation repositories # Fetching organozation repositories
def org_repos(self): def org_repos(self):
organization = input(f'{colors.white}@{colors.green}Organization{colors.white} >> {colors.reset}') organization = input(f'\n{white}[{white_bg}@Organization{reset}{white}] (username){reset} ')
api = f'https://api.github.com/orgs/{organization}/repos?per_page=100' api = f'https://api.github.com/orgs/{organization}/repos?per_page=100'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] Organization @{organization} {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red} - {white}] Organization @{organization} {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
for repo in response: for repo in response:
print(f"\n{colors.white}{repo['full_name']}{colors.reset}") print(f"\n{white}{repo['full_name']}{reset}")
for attr in self.repo_attrs: for attr in self.repo_attrs:
print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{repo[attr]}{colors.reset}") print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{repo[attr]}{reset}")
print('\n') print('\n')
# Fetching user repositories # Fetching user repositories
def user_repos(self): def user_repos(self):
username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ')
api = f'https://api.github.com/users/{username}/repos?per_page=100' api = f'https://api.github.com/users/{username}/repos?per_page=100'
response = requests.get(api) response = requests.get(api)
if response.status_code != 200: if response.status_code != 200:
print(f'\n{colors.white}[{colors.red}-{colors.white}] User @{username} {colors.red}Not Found{colors.reset}') print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}')
else: else:
response = response.json() response = response.json()
for repo in response: for repo in response:
print(f"\n{colors.white}{repo['full_name']}{colors.reset}") print(f"\n{white}{repo['full_name']}{reset}")
for attr in self.repo_attrs: for attr in self.repo_attrs:
print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{repo[attr]}{colors.reset}") print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{repo[attr]}{reset}")
print('\n') print('\n')
# Fetching user's gists # Fetching user's gists
def user_gists(self): def user_gists(self):
username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ')
api = f'https://api.github.com/users/{username}/gists' api = f'https://api.github.com/users/{username}/gists'
response = requests.get(api).json() response = requests.get(api).json()
if response == []: if response == []:
print(f'{colors.white}[{colors.red}-{colors.white}]User @{username} does not have any active gists.{colors.reset}') print(f'\n{white}[{red} - {white}] User @{username} {red_bg}does not{reset}{white} have any active gists.{reset}')
elif "Not Found" in response['message']:
print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}')
else: else:
for item in response: for item in response:
print(f"\n{colors.white}{item['id']}{colors.reset}") print(f"\n{white}{item['id']}{reset}")
for attr in self.gists_attrs: for attr in self.gists_attrs:
print(f"{colors.white}├─ {self.gists_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.gists_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Fetching user's followera' # Fetching user's followera'
def followers(self): def followers(self):
username = input(f'{colors.white}@{colors.green}Username{colors.white} >> {colors.reset}') username = input(f'\n{white}[{white_bg}@Username{reset}{white}]{reset} ')
api = f'https://api.github.com/users/{username}/followers?per_page=100' api = f'https://api.github.com/users/{username}/followers?per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
if response == []: if response == []:
print(f'\n{colors.white}[{colors.red}-{colors.white}]User @{username} does not have followers.{colors.reset}') print(f'\n{white}[{red} - {white}]User @{username} {red_bg}does not{reset}{white} have followers.{reset}')
elif "Not Found" in response['message']:
print(f'\n{white}[{red} - {white}] User @{username} {red_bg}Not Found{reset}')
else: else:
for item in response: for item in response:
print(f"\n{colors.white}@{item['login']}{colors.reset}") print(f"\n{white}@{item['login']}{reset}")
for attr in self.user_attrs: for attr in self.user_attrs:
print(f"{colors.white}├─ {self.user_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.user_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Checking whether or not user[A] follows user[B] # Checking whether or not user[A] follows user[B]
def following(self): def following(self):
user_a = input(f'{colors.white}@{colors.green}User[A]{colors.white} >> {colors.reset}') user_a = input(f'\n{white}[{white_bg}@User A{reset}{white}] (username){reset} ')
user_b = input(f'{colors.white}@{colors.green}User[B]{colors.white} >> {colors.reset}') user_b = input(f'{white}[{white_bg}@User B{reset}{white}] (username){reset} ')
api = f'https://api.github.com/users/{user_a}/following/{user_b}' api = f'https://api.github.com/users/{user_a}/following/{user_b}'
response = requests.get(api) response = requests.get(api)
if response.status_code == 204: if response.status_code == 204:
print(f'{colors.white}[{colors.green}+{colors.white}] @{user_a} follows @{user_b}.{colors.reset}') print(f'\n{white}[{green} + {white}] @{user_a} {green_bg}follows{reset}{white} @{user_b}.{reset}')
else: else:
print(f'{colors.white}[{colors.red}-{colors.white}] @{user_a} does not follow @{user_b}.{colors.reset}') print(f'\n{white}[{red} - {white}] @{user_a} {red_bg}does not{reset}{white} follow @{user_b}.{reset}')
# User search # User search
def user_search(self): def user_search(self):
query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') query = input(f'\n{white}[{white_bg}#@Query{reset}{white}]{reset} ')
api = f'https://api.github.com/search/users?q={query}&per_page=100' api = f'https://api.github.com/search/users?q={query}&per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
for item in response['items']: for item in response['items']:
print(f"\n{colors.white}@{item['login']}{colors.reset}") print(f"\n{white}@{item['login']}{reset}")
for attr in self.user_attrs: for attr in self.user_attrs:
print(f"{colors.white}├─ {self.user_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.user_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Repository search # Repository search
def repo_search(self): def repo_search(self):
query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') query = input(f'\n{white}[{white_bg}#%Query{reset}{white}]{reset} ')
api = f'https://api.github.com/search/repositories?q={query}&per_page=100' api = f'https://api.github.com/search/repositories?q={query}&per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
for item in response['items']: for item in response['items']:
print(f"\n{colors.white}{item['full_name']}{colors.reset}") print(f"\n{white}{item['full_name']}{reset}")
for attr in self.repo_attrs: for attr in self.repo_attrs:
print(f"{colors.white}├─ {self.repo_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.repo_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Topics search # Topics search
def topic_search(self): def topic_search(self):
query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') query = input(f'\n{white}[{white_bg}##Query{reset}{white}]{reset} ')
api = f'https://api.github.com/search/topics?q={query}&per_page=100' api = f'https://api.github.com/search/topics?q={query}&per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
for item in response['items']: for item in response['items']:
print(f"\n{colors.white}{item['name']}{colors.reset}") print(f"\n{white}{item['name']}{reset}")
for attr in self.topic_attrs: for attr in self.topic_attrs:
print(f"{colors.white}├─ {self.topic_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.topic_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Issue search # Issue search
def issue_search(self): def issue_search(self):
query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') query = input(f'\n{white}[{white_bg}#!Query{reset}{white}]{reset} ')
api = f'https://api.github.com/search/issues?q={query}&per_page=100' api = f'https://api.github.com/search/issues?q={query}&per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
for item in response['items']: for item in response['items']:
print(f"\n{colors.white}{item['title']}{colors.reset}") print(f"\n{white}{item['title']}{reset}")
for attr in self.issue_attrs: for attr in self.issue_attrs:
print(f"{colors.white}├─ {self.issue_attr_dict[attr]}: {colors.green}{item[attr]}{colors.reset}") print(f"{white}├─ {self.issue_attr_dict[attr]}: {green}{item[attr]}{reset}")
print('\n') print('\n')
# Commits search # Commits search
def commits_search(self): def commits_search(self):
query = input(f'{colors.white}#{colors.green}Query{colors.white} >> {colors.reset}') query = input(f'\n{white}[{white_bg}#:Query{reset}{white}]{reset} ')
api = f'https://api.github.com/search/commits?q={query}&per_page=100' api = f'https://api.github.com/search/commits?q={query}&per_page=100'
response = requests.get(api).json() response = requests.get(api).json()
n=0 number=0
for item in response['items']: for item in response['items']:
n+=1 number+=1
print(f'{colors.white}{n}.{colors.reset}') print(f'{white}{number}.{reset}')
pprint(item['commit']) pprint(item['commit'])
print('\n') print('\n')
# Update program # Update program
def update(self): def update(self):
logging.info('Fetching updates...') logging.info('Updating...')
files_to_update = ['src/main.py','lib/banner.py','lib/colors.py','octosuite','LICENSE','README.md','requirements.txt'] files_to_update = ['src/main.py','lib/banner.py','lib/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']
for file in tqdm(files_to_update,desc=f'{colors.white}[{colors.green}*{colors.white}] Fetching updates...{colors.reset}'): for file in tqdm(files_to_update,desc=f'{white}[{green} * {white}] Updating{reset}'):
data = urllib.request.urlopen(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}').read() data = requests.get(f'https://raw.githubusercontent.com/rly0nheart/octosuite/master/{file}')
with open(file, 'wb') as code: with open(file, 'wb') as code:
code.write(data) code.write(data.content)
code.close() code.close()
logging.info('Update complete.') logging.info('Update complete.')
exit(f'\n{colors.white}[{colors.green}+{colors.white}] Update complete. Re-run octosuite.{colors.reset}') exit(f'{white}[{green} + {white}] {green_bg}Updated{reset}{white} successfully. Re-run octosuite.{reset}')
# Show changelog # Show changelog
def changelog(self): def changelog(self):
# lol yes the changelog is hard coded # lol yes the changelog is hard coded
changelog_text = ''' changelog_text = f'''
v1.3.0 Changelog:
{red_bg}v1.5.2-alpha [CHANGELOG]{reset}
• Adjusted to work on Mac • Users will now get to choose whether to enable colors or not
• Added changelog command • Cleaned code
• Added progress bar when fetching updates • Improved perfomance
• Minor bug fixes • Will be ignoring unknown commands instead of printing the error
''' • Major bug fixes
return changelog_text {red_bg} {reset}
'''
print(changelog_text)
# Author info # Author info
def author(self): def author(self):
print(f'\n{colors.white}Richard Mwewa (Ritchie){colors.reset}') print(f'\n{white}Richard Mwewa (Ritchie){reset}')
for key,value in self.author_dict.items(): for key,value in self.author_dict.items():
print(f'{colors.white}├─ {key}: {colors.green}{value}{colors.reset}') print(f'{white}├─ {key}: {green}{value}{reset}')
def exit_session(self):
logging.info('Session closed with \'exit\' command.')
exit(f'\n{white}[{green} ! {white}] Session closed with {white_bg}exit{reset}{white} command.{reset}')
def help(self): def help(self):
help = ''' help = f'''
usage: {red_bg}[COMMAND] [DESCRIPTION] {reset}
orginfo --> Get target organization info info:org Get target organization info
userinfo --> Get target user profile info info:user Get target user profile info
repoinfo --> Get target repository info info:repo Get target repository info
pathcontents --> Get contents of a specified path from a target repository info:dev Show developer's info
orgrepos --> Get a list of repositories owned by a target organization path:contents Get contents of a specified path from a target repository
userrepos --> Get a list of repositories owned by a target user repos:org Get a list of repositories owned by a target organization
usergists --> Get a list of gists owned by a target user repos:user Get a list of repositories owned by a target user
userfollowers --> Get a list of the target's followers user:gists Get a list of gists owned by a target user
userfollowing --> Check whether or not User[A] follows User[B] user:followers Get a list of the target's followers
usersearch --> Search user(s) user:following Check whether or not User[A] follows User[B]
reposearch --> Search repositor[y][ies] search:users Search user(s)
topicsearch --> Search topic(s) search:repos Search repositor[y][ies]
issuesearch --> Search issue(s) search:topics Search topic(s)
commitsearch --> Search commit(s) search:issues Search issue(s)
update --> Update octosuite search:commits Search commit(s)
changelog --> Show changelog update Update octosuite
author --> Show author info changelog Show changelog
help --> Show usage/help help Show usage/help
exit --> Exit session exit Exit session
{red_bg} {reset}
''' '''
return help print(help)
# If .logs folder exists, 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
if platform.system().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 # Set to automatically monitor and log network and user activity into the .logs folder
logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='[%(asctime)s] %(message)s',level=logging.DEBUG) logging.basicConfig(filename=f'.logs/{datetime.now()}.log',format='[%(asctime)s] [%(levelname)s] %(message)s',datefmt='%Y-%m-%d %H:%M:%S%p',level=logging.DEBUG)