Compare commits

...

66 Commits
v1.6.6 ... main

Author SHA1 Message Date
Neocky
6ea80b9c66
Merge pull request #67 from Neocky/v1.7.3
V1.7.3 - Fixed FTP error while checking
2023-02-12 18:28:32 +01:00
Neocky
3f4380acae Update handle_ftp.py 2023-02-09 20:58:17 +01:00
Neocky
a9bac687c8 Revert false FTP fix and solve it 2023-02-09 20:54:03 +01:00
Neocky
1fda453bbd Fixed issue with ftp temp folder
The ftp files wouldn't get downloaded in the TempSFTPFolder and this would result in a crash
2023-02-09 20:18:21 +01:00
Neocky
30f0dd91ac
Merge pull request #59 from treier57lol/pluget-logo
New pluGET logo
2022-07-20 09:07:34 +02:00
treier57lol
c8b026fb3a
Changed logo in README 2022-07-19 12:30:10 +03:00
treier57lol
b6b5b4dff7
Added new logo as image files 2022-07-19 12:29:43 +03:00
treier57lol
cd9479d8d5
Added new pluGET logo 2022-07-19 12:04:20 +03:00
Neocky
88b897716a
Changed version 2022-07-19 10:42:02 +02:00
Neocky
a7b528d25d
Merge pull request #57 from Neocky/dev/1.7.1
Dev/1.7.2
2022-07-19 10:30:04 +02:00
Jan-Luca Bogdan | BEL NET GmbH
1c11b0df83 Delete install_requirements.py 2022-07-19 10:29:34 +02:00
Neocky
653dac6c2c
Merge pull request #54 from smcclennon/dev/1.7.1
Handle Spiget api errors, use best-practice, disable incompatible code
2022-07-19 10:22:37 +02:00
Jan-Luca Bogdan | BEL NET GmbH
a5eeca7d9a Changed formating and print method
- Changed formating to have no lines longer than 120 characters
- replaced print() with richt_print_error() to print formatted error message
2022-07-19 10:17:59 +02:00
Neocky
1fc53d907d
Merge pull request #56 from smile-minecraft/patch-1
update README.md
2022-07-19 10:06:05 +02:00
smile-minecraft
32aabefdf1
update README.md
velocity not waterfall
2022-07-16 13:07:01 +08:00
Shiraz
18ca3104a5 Handle Spiget api errors
so plugins with special characters such as "InvSee++" don't crash pluGET
due to unexpected Spiget api data structure response:
{'error': '', 'msg': ''}

Root cause may be special chracters not being URL-escaped by Spiget
2022-07-09 19:59:44 +01:00
Shiraz
8227c12bf1 Only rename console title on Windows
Prevent "sh: line 1: title: command not found" error on Linux
2022-07-09 10:17:21 +01:00
Shiraz
18e012540c Pip install on posix using python3 -m
as per the official documentation:
packaging.python.org/en/latest/tutorials/installing-packages/#id22
2022-07-09 10:14:18 +01:00
Shiraz
526ccf72cb Show error message on requirements install failure 2022-07-09 10:07:34 +01:00
Neocky
4250ab15df
Added purpur help 2022-07-04 16:08:15 +02:00
Neocky
03a117c4e8
Merge pull request #51 from Pazdikan/readme-rewrite-and-new-installation
Cleaned up README.md and changed installation files
2022-07-04 16:05:33 +02:00
Neocky
ede3557819
Merge pull request #53 from Neocky/dev/1.7.1
Dev/1.7.1
2022-07-04 16:04:55 +02:00
Jan-Luca Bogdan | BEL NET GmbH
0e09daae53 Added new help command
- added new help command
- fixed some formating issues
2022-07-04 15:55:08 +02:00
Jan-Luca Bogdan | BEL NET GmbH
6b926c9564 Added plugin remover 2022-07-04 14:43:26 +02:00
pazdikan
b539d11f67 Reverted formatting changes 2022-07-04 14:29:59 +02:00
pazdikan
744f2e5988 Fixed a typo 2022-07-04 14:17:23 +02:00
Jan-Luca Bogdan | BEL NET GmbH
119d44631f Added support for purpur serverjar updating
- Added support for purpur updating and checking #43
- Removed get_installed_mc_version function which isn't needed anymore
2022-07-04 12:36:34 +02:00
pazdikan
626b7a3812 Cleaned up README.md and changed installation files 2022-07-03 01:39:20 +02:00
Neocky
1df6b36c5d
Update README.md 2022-07-02 19:52:40 +02:00
Neocky
d7c09eb04a
Update README.md 2022-07-02 19:43:52 +02:00
Neocky
bac69ae829
Update README.md 2022-07-02 19:43:02 +02:00
Neocky
badf6230bb
Merge pull request #50 from Neocky/dev/1.7.0
Dev/1.7.0 should work. Tests were all successfull
2022-07-02 19:29:14 +02:00
Neocky
2e9973a24f
Renamed main file 2022-07-02 19:23:22 +02:00
Neocky
0bac45d478 Renamed main file and removed old src 2022-07-02 19:21:14 +02:00
Neocky
2ba9e6ddb7 Added serverjar updating
Added serverjar updating: paper, waterfall, velocity
2022-07-02 19:15:15 +02:00
Jan-Luca Bogdan | BEL NET GmbH
9e74d480dd Added first serverjar handling 2022-06-30 16:45:28 +02:00
Jan-Luca Bogdan | BEL NET GmbH
0b10a76a3b Added version lookup for egg_cracking_jar 2022-06-29 16:00:55 +02:00
Jan-Luca Bogdan | BEL NET GmbH
9b2ff6e208 Added egg cracking jar support 2022-06-28 17:02:33 +02:00
Neocky
e17be2c500 Added tests for function in update_checker 2022-06-26 18:11:19 +02:00
Jan-Luca Bogdan | BEL NET GmbH
fb52973cf6 Added seperate download function for sftp/ftp 2022-06-22 15:26:45 +02:00
Jan-Luca Bogdan | BEL NET GmbH
3219e32351 Added part of update functionality 2022-06-22 12:58:48 +02:00
Jan-Luca Bogdan | BEL NET GmbH
041d7aa240 Added sftp & ftp functionality back 2022-06-21 17:01:03 +02:00
Jan-Luca Bogdan | BEL NET GmbH
96b4411020 Added better error handling to download function 2022-06-21 09:57:52 +02:00
Jan-Luca Bogdan | BEL NET GmbH
8620dfbe74 Added experimental update checker 2022-06-20 17:21:58 +02:00
Jan-Luca Bogdan | BEL NET GmbH
84fcfc8922 Added new spiget downloader
Added new spiget downloader for plugins with progress bar and error handling for Issue #46
2022-06-17 12:51:32 +02:00
Neocky
d427886c49 Added search function 2022-06-16 22:35:54 +02:00
Neocky
e229324117 Changed indention from tab to space 2022-06-16 21:26:43 +02:00
Jan-Luca Bogdan | BEL NET GmbH
7f2404f0cd Created packages, testcases and better error handling
Created python packages and added first test cases. Added better error handling when api call returns None and created start of requirements function.
Moved rich_print_error() to console_output.py
2022-06-16 17:05:40 +02:00
Neocky
3943357569 Added plugin downloader capabilities
Added plugin downloader capabilities and added full console argument support.
Fixed #46 and added error handling for it
2022-06-12 20:03:36 +02:00
Neocky
ac139ed048 Added input handling and first utilities 2022-06-07 22:40:27 +02:00
Neocky
8e51b1976d Added OS independent installer and finished console output 2022-06-04 16:33:46 +02:00
Neocky
4920689f0e finished config handling and started new console output
Finished the yaml config handling and started console handling output
2022-06-04 00:08:10 +02:00
Neocky
993d438ff7 Started the new config handling
Implemented new and not complete config handling and implemented command parameter support
2022-06-02 19:52:49 +02:00
Neocky
3faf9785d7
Update FUNDING.yml 2022-05-08 19:23:31 +02:00
Neocky
17e67e68ea
Update README.md 2022-05-08 19:21:10 +02:00
Neocky
5075922674
Added new button 2022-04-18 16:27:04 +02:00
Neocky
cf710088a9
Merge pull request #45 from ProfessorSniff/#!/bin/sh
add shebang to launcher.sh
2021-12-05 16:53:44 +01:00
ProfessorSniff
b1b5eeb992
add shebang to launcher.sh 2021-12-03 04:02:24 +00:00
Neocky
3bcb3de4cf Fixed error when searching for plugins
Changes:
- fixed bug when searching for plugins where the name couldn't be sanitized
- fixed bug when searching for plugins with numbers in name
2021-08-26 19:24:17 +02:00
Neocky
9a70733421 Changed description for FTP Port support 2021-08-07 18:58:23 +02:00
Neocky
449232e2ca
Merge pull request #38 from TheDudeFromCI/patch-1
Added custom port support for FTP access.
2021-08-07 18:50:18 +02:00
TheDudeFromCI
1c74d8501c
Apply suggestions from code review
Co-authored-by: Neocky <Neocky@users.noreply.github.com>
2021-08-04 19:52:10 -07:00
TheDudeFromCI
9289872831
Removed warning in default config 2021-08-03 03:01:51 -07:00
TheDudeFromCI
0c3bace49e
Added custom port support for ftp access 2021-08-03 03:00:23 -07:00
Neocky
2e459cafe2 Fixed spelling
Changes:
- fixed uppercase variable name
2021-08-01 15:10:36 +02:00
Neocky
1ff34a7372 Fixed ftp file or folder check
Changes:
- fixed issue where it couldn't detect if it would be a file or folder with ftp
2021-08-01 15:06:55 +02:00
36 changed files with 2709 additions and 1682 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,3 +1,3 @@
# These are supported funding model platforms
custom: https://www.buymeacoffee.com/Neocky # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
ko_fi: Neocky

3
.gitignore vendored
View File

@ -133,3 +133,6 @@ dmypy.json
# PyCharm Settings
.idea
# pluGET Files
pluGET_config.yaml

280
README.md
View File

@ -1,7 +1,11 @@
<p align="center">
<img src="https://i.ibb.co/JyCxnQn/logoreal.png" alt="pluGET" border="0"></a>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./assets/branding/pluget-logo-white.png">
<source media="(prefers-color-scheme: light)" srcset="./assets/branding/pluget-logo-black.png">
<img src="./assets/branding/pluget-logo-black.png" alt="pluGET" border="0">
</picture>
</p>
<p align="center">
@ -17,12 +21,12 @@
# pluGET
#### A powerful package manager which updates [Plugins](https://www.spigotmc.org/resources/) and Server Software for minecraft servers.
A powerful package manager which updates [Plugins](https://www.spigotmc.org/resources/) and Server Software for minecraft servers.
<img src="https://www.bildhost.com/images/2021/06/27/ezgif-1-28e102202188.gif" alt="pluGET.gif" border="0" />
<img src="https://user-images.githubusercontent.com/13088544/177011216-1360d444-278a-475e-9863-966c48c60ba7.gif" alt="pluGET.gif" border="0" />
<details>
<summary>Screenshots</summary>
<summary>Old Screenshots</summary>
`check all` to check installed plugins for updates:
![screenshot1](https://i.ibb.co/QM7xh7w/pluget-checkall-small.png)
@ -35,43 +39,24 @@
</details>
# About
pluGET is a standalone package manager written in python for minecraft [Spigot](https://www.spigotmc.org/) servers and its forks (e.g. [PaperMC](https://papermc.io/)). The program works with a locally installed servers or with a remote host through SFTP/FTP, when configured in the config. It uses the [Spiget](https://spiget.org/) API to download and compare plugin versions and download the latest version of plugins from the [Spigot](https://www.spigotmc.org/) site. It can also compare and download the latest update of specific server software (e.g. [PaperMC](https://papermc.io/)).
Plugin management is the hard part of managing a minecraft server. The time it takes to manually check the [Spigot resources](https://www.spigotmc.org/resources/) page for updates and manually downloading all plugins is too long and daunting. So I built pluGET to automate and ease the plugin handling of a minecraft server and to turn the most time consuming part of managing a minecraft server to an easy one.
This program is suited for minecraft server owners who want to save time and stay on top of their plugin versions. The program input and the associated config file are pretty simple so every server owner and not only the most tech savy ones can use pluGET to ease their plugin handling.
<img src="https://i.ibb.co/82dnyrK/image.png" alt="meme" border="0" height="350" width="350"></a>
## Issues? Found a bug?
[Create an issue.](https://github.com/Neocky/pluGET/issues/new/choose)
## About
This is a package manager for minecraft [Spigot](https://www.spigotmc.org/) servers and its forks (e.g. [PaperMC](https://papermc.io/)).
This is a standalone program written in python.
The program works with a locally installed server or with a remote host through SFTP/FTP, when configured in the config.
It uses the [Spiget](https://spiget.org/) API to download and compare plugin versions and can download the latest version of plugins from the [Spigot](https://www.spigotmc.org/) site.
It can also compare and download the latest update of specific server software (e.g. [PaperMC](https://papermc.io/)).
Plugin management was the hard part of managing a minecraft server. The time it took to check the [Spigot resource](https://www.spigotmc.org/resources/) page for updates for the installed plugins and updating all plugins manually which have available updates was too long and daunting.
So I built pluGET to automate and ease the plugin handling of a minecraft server and to turn the most time consuming part of managing a minecraft server to an easy one.
This program is suited for minecraft server owners who want to save time and stay on top of their plugin versions.
The program input and the associated config file are pretty simple so every server owner and not only the most tech savy ones can use pluGET to ease their plugin handling.
Follow the [Installation](https://github.com/Neocky/pluGET#installation) guide below for an easy and hassle free setup of pluGET.
Read [Usage](https://github.com/Neocky/pluGET#usage) below to get some example inputs when using pluGET.
If you still have questions [here](https://github.com/Neocky/pluGET#need-help) is the best place to ask for support.
So what can it do exactly?
pluGET can:
- work locally or through SFTP/FTP
- manage plugins:
- download the latest version of a plugin
- update every installed/one specific plugin
- check for an update of every installed/one specific plugin
- remove a plugin from the plugin folder
- manage server software:
- download a specific server software version
- check installed server software for update
- update installed server software to a specific version
- supported server software: [PaperMc](https://papermc.io/)
# Features
- Works locally or through SFTP/FTP
- Runs directly from the console with command line arguments
- Checks for updates and downloads the latest version of all/specific plugins
- Checks for updates and downloads the latest version of your server software
- [PaperMc](https://papermc.io/)
- [Purpur](https://purpurmc.org/)
- [Waterfall](https://papermc.io/downloads#Waterfall)
- [Velocity](https://papermc.io/downloads#Velocity)
There are more features in the work. Check [Projects](https://github.com/Neocky/pluGET/projects) for a complete list.
@ -79,183 +64,160 @@ There are more features in the work. Check [Projects](https://github.com/Neocky/
[Get the latest release here.](https://github.com/Neocky/pluGET/releases)
## Donations
If you feel like showing your love and/or appreciation for this project then how about buying me a coffee! :)
<a href="https://www.buymeacoffee.com/Neocky" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="76" width="272"></a>
# Donations
If you feel like showing your love and/or appreciation for this project then how about buying me a coffee? ☕🤎
[<img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="ko-fi" width="300"/>](https://ko-fi.com/Y8Y1CKZ43)
## Need help?
[<img src="https://i.ibb.co/CMKbT0L/rsz-1rsz-discord.png" alt="Discord" width="272"/>](https://discord.gg/475Uf4NBPF)
# Need help?
[<img src="https://i.ibb.co/PQv3KtJ/Discord-Logo-Wordmark-Color.png" alt="Discord" width="300"/>](https://discord.gg/475Uf4NBPF)
## Installation
### 1. Python
Python needs to be installed on your machine.
Get it [here](https://www.python.org/downloads/).
### 2. Dependencies
Install the needed packages for this project.
#### Automatically (Windows only)
Execute the `installer.bat` file to automaticcally install the needed packages for this project.
> Sometimes the security warning `Windows protected your PC` comes when launching the `installer.bat` file.
> This is a normal behaviour from the windows defender because this is a unknown `.bat` file.
> To run the `installer.bat` anyway, click `More Info` and then `Run anyway` when the message pops up.
# Installation
## 1. Python 3.10.4
Python needs to be installed on your machine. Get it [here](https://www.python.org/downloads/).
#### Manually
Execute this command in the `\plugGET` folder:
```python
py -m pip install -r requirements.txt
```
## 2. Dependencies
In order to install dependencies run the `install_requirements_WINDOWS/LINUX` file, of course depending on your system.
### 3. Edit the Config
When run the first time, the `config.ini` file will be created in the `\src` folder and the program will close.
## 3. Edit the config
When run pluGET for the first time, the `pluGET_config.yaml` file will be created in the main folder and the program will close.
Edit the config to your needs and relaunch pluGET.
**Now you are good to go!**
## Start pluGET
### Windows:
Execute the `launcher.bat` in the `\pluGET` folder.
## 4. Running the program
Execute the `pluget.py` file with python in the `\pluGET` folder.
This will launch pluGET correctly.
> Sometimes the security warning `Windows protected your PC` comes when launching the `launcher.bat` file.
> This is a normal behaviour from the windows defender because this is a unknown `.bat` file.
> To run the `launcher.bat` anyway, click `More Info` and then `Run anyway` when the message pops up.
### Linux:
Use `cd` to change into the `/pluGET` directory and change the permission of the `launcher.sh` to make it executeable:
```
$ chmod +x launcher.sh
```
Execute the `launcher.sh` file:
```
$ ./launcher.sh
```python
# Windows:
py pluget.py
# Linux
python3 pluget.py
```
> On both OS you can also launch the `src/__main__.py` file.
## Usage
# Usage
> As always, if you update plugins, shut down your server!
The following are examples of input for the general usage:
(Hint: [thingsInBrackets] are optional & 'all' can always be exchanged through the plugin name or the plugin id and reverse)
## Commands:
### General
#### Command help:
`help command [all/command]`
```
help command
```
<details>
### • Show the information about all commands.
<!-- <details>
<summary>Output</summary>
![Output](https://i.ibb.co/9VZCjD6/pluget-help2.png)
![Example output](https://i.ibb.co/9VZCjD6/pluget-help2.png)
</details>
</details> -->
### Manage Plugins
#### Download the latest update of a specific package:
`get [pluginID/pluginName]`
```
get 'pluginID'
```
or:
help command [all/command]
```
get 'pluginName'
```
#### Check all plugins/one specific plugin for updates with optional changelog output:
`check [all/pluginName] [changelog]`
### • Exit program:
```
check all
```
or:
exit .
```
check 'pluginName' changelog
### • Get link to this page:
```
help .
```
## Manage Plugins
### • Download the latest update of a specific package:
```
get [pluginID/pluginName]
```
<details>
### • Check all plugins/one specific plugin for updates with optional changelog output:
<!-- <details>
<summary>Output</summary>
![Output](https://i.ibb.co/VmSNh6K/pluget-checkall.png)
</details>
</details> -->
```
check [all/pluginName] [changelog]
```
#### Update all plugins/one specific plugin:
`update [all/pluginName]`
### • Update all plugins/one specific plugin:
```
update all
update [all/pluginName]
```
or:
### • Remove a plugin with the ID/Name:
```
update 'pluginName'
```
#### Remove a plugin with the ID/Name:
`remove [pluginID/pluginName]`
remove [pluginID/pluginName]
```
remove 'pluginID'
```
or:
### • Search for a plugin:
```
remove 'pluginName'
```
#### Search for a plugin:
`search [pluginName]`
search [pluginName]
```
search 'pluginName'
```
#### Exit program:
`exit [anything]`
```
exit .
```
#### Get link to here:
`help [anything]`
```
help .
```
### Manage Server Software
#### Check installed server software for updates:
`check serverjar`
## Manage Server Software
### • Check installed server software for updates:
```
check serverjar
```
### Update installed server software to latest/specific version:
`update serverjar [Version]`
### • Update installed server software to latest/specific version:
```
update serverjar 'PaperMCVersion'
```
### Download specific paper version:
`get-paper [paperBuild] [minecraftVersion]`
```
get-paper 550 1.16.5
```
or:
```
get-paper 321
update serverjar [Version]
```
## Known problems
### • Download specific paper version:
```
get-paper [paperBuild] [minecraftVersion]
```
### Can't get latest version/Update available
### • Download specific waterfall version:
```
get-waterfall [waterfallBuild] [minecraftVersion]
```
#### Inconsistent Names and Versions
For example:
### • Download specific velocity version:
```
get-velocity [velocityBuild] [minecraftVersion]
```
### • Download specific purpur version:
```
get-purpur [purpurBuild] [minecraftVersion]
```
# Command line arguments
pluGET supports all commands directly through the command line. Get the list of all available command line arguments with the `-h` argument.
Example direct command line call:
```shell
py pluget.py check all
```
# Known problems
## Can't get latest version/Update available
### Inconsistent Names and Versions
Example:
![EssentialsX](https://i.ibb.co/fDyCYQ8/essentialsx.png)
EssentialsX is a prominent example of inconsisten version naming. The installed version is `2.18.2.0` but on [Spigot](https://www.spigotmc.org/resources/essentialsx.9089/update?update=371379) the version is only described as `2.18.2`.
That's the reason pluGET can't detect it automatically.
> There are of course many more plugins which have some sort of inconsistency which makes it sadly impossible for pluGET to detect them all. EssentialsX is used only as an example.
#### Solution
### Solution
Download the plugins with the `get [pluginName]` command to make them detectable for pluGET.
After downloading EssentialsX with `get EssentialsX` and using `check all`:
![EssentialsX](https://i.ibb.co/ws5wHTj/essentialsx-2.png)
EssentialsX is now detected from pluGET and can update automatically when a new version comes out.
#### Bukkit plugins
For example:
### Bukkit plugins
Example:
![worldguard](https://i.ibb.co/7NJ9HRG/pluget-checkallonlyone.png)
As you can see the installed version was found but not the latest version for this plugin.
This is because this is a plugin which is not available on [Spigot](https://www.spigotmc.org/resources/).
pluGET supports currently only plugins from [Spigot](https://www.spigotmc.org/resources/).
In this example this is a bukkit plugin.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
sodipodi:docname="pluget-logo.svg"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
xml:space="preserve"
id="svg5"
version="1.1"
viewBox="0 0 101.31679 30.525789"
height="30.525789mm"
width="101.31679mm"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.8284271"
inkscape:cx="60.811183"
inkscape:cy="81.31728"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs2"><inkscape:path-effect
effect="mirror_symmetry"
start_point="66.717927,194.91922"
end_point="66.717927,196.6932"
center_point="66.717927,195.80621"
id="path-effect2543"
is_visible="true"
lpeversion="1.2"
lpesatellites=""
mode="free"
discard_orig_path="false"
fuse_paths="false"
oposite_fuse="false"
split_items="false"
split_open="false"
link_styles="false" /></defs><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-244.05501,27.440776)"><path
style="display:inline;fill:none;stroke:#ffffff;stroke-width:1.08823;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 269.92243,-15.41098 0.89279,-0.51545 c 2.31137,-1.33446 4.26969,-0.85065 3.74023,2.09491 -0.52947,2.94556 -2.57765,6.63243 0.19723,7.52795 1.17498,0.3792 3.00732,-0.24884 3.52934,-2.54794 0.70271,-3.09482 -0.37661,-6.7641 -1.62003,-10.38648 l -0.87139,-2.53855"
id="path2515"
sodipodi:nodetypes="cszsssc"
inkscape:label="wire" /><g
id="g2525"
transform="matrix(1.5194714,-0.52157949,0.52157949,1.5194714,111.14245,-282.32695)"
style="display:inline;stroke:none"
inkscape:label="plug"><g
id="g2521"
transform="matrix(1.8835345,0,0,1.8835345,-81.40728,-180.76428)"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:label="pins"><rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.744316;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2517"
width="0.39793873"
height="1.5362287"
x="66.120705"
y="193.25238"
ry="0.19896936"
inkscape:label="left" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.744316;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2519"
width="0.39793873"
height="1.5362287"
x="66.940681"
y="193.25238"
ry="0.19896936"
inkscape:label="right" /></g><path
id="path2523"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.439285;stroke-linecap:round"
d="m 65.710577,195.23133 c 0,0.68804 0.25868,1.46187 1.00735,1.46187 v -1.77398 c -0.889087,0 -1.00735,0 -1.00735,0.31211 z m 2.0147,0 c 0,0.68804 -0.25868,1.46187 -1.00735,1.46187 v -1.77398 c 0.889087,0 1.00735,0 1.00735,0.31211 z"
sodipodi:nodetypes="cccc"
class="UnoptimicedTransforms"
transform="matrix(1.8835345,0,0,1.8835345,-81.385177,-182.13354)"
inkscape:label="body"
inkscape:original-d="m 65.710577,195.23133 c 0,0.68804 0.25868,1.46187 1.00735,1.46187 v -1.77398 c -0.889087,0 -1.00735,0 -1.00735,0.31211 z"
inkscape:path-effect="#path-effect2543" /></g><path
id="path2531"
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.989574;stroke-linejoin:round"
class="UnoptimicedTransforms"
inkscape:label="top side"
inkscape:transform-center-x="5.3509257"
inkscape:transform-center-y="-7.3394763"
transform="matrix(0.95451618,0,0,0.95451618,236.87111,-259.69297)"
d="m 15.626149,255.26898 5.938704,3.42871 13.318093,-7.68921 -5.938698,-3.42871 z m -7.3793868,-4.26049 5.9804058,3.45279 13.318085,-7.68921 -5.980399,-3.45279 z"
sodipodi:nodetypes="cccccccccc" /><path
transform="matrix(-0.47725809,-0.82663525,0.82663525,-0.47725809,53.21073,137.01425)"
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.989574;stroke-linejoin:round"
class="UnoptimicedTransforms"
id="path2537"
d="M 14.905808,254.85309 28.223899,262.5423 41.541992,254.85309 28.2239,247.16388 Z"
inkscape:label="blank side" /><path
id="path2533"
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.989574;stroke-linejoin:round"
class="UnoptimicedTransforms"
d="m 14.905755,254.85285 13.31825,7.68919 13.317866,-7.68907 -13.318249,-7.6892 z m 4.297769,-0.63465 2.921445,-1.6867 3.373173,1.94776 1.460956,-0.84349 -1.686685,-0.9735 4.381932,-2.52991 1.686685,0.97351 -1.460956,0.84348 3.373642,1.94748 1.460488,-0.84321 1.686685,0.97351 -4.381932,2.52991 -1.686686,-0.97351 -1.460487,0.84321 3.373173,1.94776 -2.921444,1.6867 -3.373174,-1.94776 2.921445,-1.6867 -3.373642,-1.94748 -2.921445,1.6867 z"
transform="matrix(-0.47725809,0.82663526,-0.82663526,-0.47725809,488.59969,90.37568)"
inkscape:label="creeper face side" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.0938px;line-height:1.25;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.427346"
x="281.58862"
y="-7.4172397"
id="text2529"
inkscape:label="text"><tspan
sodipodi:role="line"
id="tspan2527"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:17.0938px;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;fill-opacity:1;stroke-width:0.427346"
x="281.58862"
y="-7.4172397"
dx="0 -0.51999998 -0.18000001 -0.69 -0.5 0.57999998">pluGET</tspan></text></g></svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,4 @@
#!/bin/bash
echo "Installing Python packages and dependencies from requirements.txt... Please wait."
pip install -r requirements.txt

View File

@ -0,0 +1,4 @@
@echo off
title Installing requirements...
echo Installing Python packages and dependencies from requirements.txt... Please wait.
py -m pip install -r requirements.txt

View File

@ -1,34 +0,0 @@
@ECHO OFF
cd /d %~dp0
Echo ___ ___ ___ ___ ___ ___
Echo /\ \ /\__\ /\__\ /\ \ /\ \ /\ \
Echo /::\ \ /:/ / /:/ / /::\ \ /::\ \ \:\ \
Echo /:/\:\ \ /:/ / /:/ / /:/\:\ \ /:/\:\ \ \:\ \
Echo /::\~\:\ \ /:/ / /:/ / ___ /:/ \:\ \ /::\~\:\ \ /::\ \
Echo /:/\:\ \:\__\ /:/__/ /:/__/ /\__\ /:/__/_\:\__\ /:/\:\ \:\__\ /:/\:\__\
Echo \/__\:\/:/ / \:\ \ \:\ \ /:/ / \:\ /\ \/__/ \:\~\:\ \/__/ /:/ \/__/
Echo \::/ / \:\ \ \:\ /:/ / \:\ \:\__\ \:\ \:\__\ /:/ /
Echo \/__/ \:\ \ \:\/:/ / \:\/:/ / \:\ \/__/ \/__/
Echo \:\__\ \::/ / \::/ / \:\__\
Echo \/__/ \/__/ \/__/ \/__/
Echo `
Echo `
Echo ------------------------------------
Echo [By Neocky]
Echo https://github.com/Neocky/pluGET
Echo pluGET-Installer
Echo ------------------------------------
Echo `
Echo ----------------------------------------------------------------------------------
Echo Installing Python packages and dependencies...
py -m pip install -r requirements.txt
Echo `
Echo Launching pluGET...
launcher.bat
exit

View File

@ -1,4 +0,0 @@
@ECHO OFF
cd "%~dp0"
cd src
py "__main__.py"

View File

@ -1,2 +0,0 @@
cd src/
python3 "__main__.py"

45
pluget.py Normal file
View File

@ -0,0 +1,45 @@
"""
Handles the main function and the argument passing for the whole pluGET program
"""
import sys
import argparse
# check if folder 'src' is accessible with all modules needed and if not exit
try:
from src.handlers.handle_config import check_config, validate_config
from src.utils.console_output import rename_console_title, clear_console, print_logo, print_console_logo
from src.utils.utilities import check_requirements, api_test_spiget, check_for_pluGET_update
from src.handlers.handle_input import handle_input
except TypeError:
print("Folder 'src' not found in the directory or missing files or broken functions detected! \
\nPlease redownload the files from here: https://www.github.com/Neocky/pluGET")
sys.exit()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Arguments for pluGET",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("mode", help="Mode (install/update/etc.)", nargs='?', default=None)
parser.add_argument("object", help="Object/Plugin Name", nargs='?', default=None)
parser.add_argument("version", help="Version", nargs='?', default=None)
parser.add_argument("--no-confirmation", action="store_true", help="Skip confirmation messages")
args = vars(parser.parse_args())
rename_console_title()
check_config()
validate_config()
api_test_spiget()
check_requirements()
if args["mode"] is not None and args["object"] is not None:
# arguments were used so call the handle_input function to get the right function call
print_console_logo()
check_for_pluGET_update()
handle_input(args["mode"], args["object"], args["version"], args["no_confirmation"], arguments_from_console=True)
else:
# no arguments were used so start pluGET console
clear_console()
print_logo()
check_for_pluGET_update()
handle_input()

View File

@ -11,4 +11,5 @@ pysftp >= 0.2.9
rich >= 9.13.0
commonmark >= 0.9.1
Pygments >= 2.8.1
typing_extensions >= 3.7.4.3
typing_extensions >= 3.7.4.3
ruamel.yaml >= 0.17.21

View File

@ -1,16 +0,0 @@
from utils.consoleoutput import consoleTitle, clearConsole, printMainMenu
from utils.utilities import check_requirements
from handlers.handle_input import createInputLists, getInput
from handlers.handle_config import checkConfig
def mainFunction():
consoleTitle()
clearConsole()
checkConfig()
check_requirements()
createInputLists()
printMainMenu()
getInput()
mainFunction()

View File

@ -1,87 +1,109 @@
""""
Handles the logic for the config validation, reading and creating
"""
import os
import sys
import configparser
import ruamel.yaml
from pathlib import Path
from utils.consoleoutput import oColors
from rich.console import Console
class configurationValues:
def __init__(self):
config = configparser.ConfigParser()
config.sections()
config.read("config.ini")
localPluginFolder = config['General']['UseLocalPluginFolder']
self.pathToPluginFolder = Path(config['Local - This Machine']['PathToPluginFolder'])
seperateDownloadPath = config['Local - This Machine']['SeperateDownloadPath']
self.pathToSeperateDownloadPath = Path(config['Local - This Machine']['PathToSeperateDownloadPath'])
self.sftp_server = config['SFTP - Remote Server']['Server']
self.sftp_user = config['SFTP - Remote Server']['Username']
self.sftp_password = config['SFTP - Remote Server']['Password']
sftp_port = config['SFTP - Remote Server']['SFTPPort']
self.sftp_folderPath = config['SFTP - Remote Server']['PluginFolderOnServer']
sftp_useSftp = config['SFTP - Remote Server']['USE_SFTP']
sftp_seperateDownloadPath = config['SFTP - Remote Server']['SeperateDownloadPath']
self.sftp_pathToSeperateDownloadPath = config['SFTP - Remote Server']['PathToSeperateDownloadPath']
self.sftp_port = int(sftp_port)
if localPluginFolder == 'True':
self.localPluginFolder = True
else:
self.localPluginFolder = False
if seperateDownloadPath == 'True':
self.seperateDownloadPath = True
else:
self.seperateDownloadPath = False
if sftp_seperateDownloadPath == 'True':
self.sftp_seperateDownloadPath = True
else:
self.sftp_seperateDownloadPath = False
if sftp_useSftp == 'True':
self.sftp_useSftp = True
else:
self.sftp_useSftp = False
class config_value():
"""
Class which holds all the available configuration values from the config file and which will be used later in
the process of updating plugins
If bool in config can't be read it will default to 'False'
"""
def __init__(self):
yaml = ruamel.yaml.YAML()
with open("pluGET_config.yaml", "r") as config_file:
data = yaml.load(config_file)
self.connection = str(data["Connection"]).lower()
self.path_to_plugin_folder = Path(data["Local"]["PathToPluginFolder"])
self.local_seperate_download_path = True if data["Local"]["SeperateDownloadPath"] == True else False
self.local_path_to_seperate_download_path = Path(data["Local"]["PathToSeperateDownloadPath"])
self.server = data["Remote"]["Server"]
self.username = data["Remote"]["Username"]
self.password = data["Remote"]["Password"]
self.sftp_port = int(data["Remote"]["SFTP_Port"])
self.ftp_port = int(data["Remote"]["FTP_Port"])
self.remote_seperate_download_path = True if data["Remote"]["SeperateDownloadPath"] == True else False
self.remote_path_to_seperate_download_path = data["Remote"]["PathToSeperateDownloadPath"]
self.remote_plugin_folder_on_server = data["Remote"]["PluginFolderOnServer"]
def checkConfig():
configAvailable = os.path.isfile("config.ini")
if not configAvailable:
createConfig()
print(oColors.brightRed + "Config created. Edit config before executing again!" + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
def check_config() -> None:
"""
Check if there is a pluGET_config.yml file in the same folder as pluget.py and if not create a new config
and exit the programm
"""
if not os.path.isfile("pluGET_config.yaml"):
create_config()
return None
def createConfig():
config = configparser.ConfigParser(allow_no_value=True)
config['General'] = {}
config['General'][';'] = 'If a local plugin folder exists (True/False) (If False SFTP/FTP will be used):'
config['General']['UseLocalPluginFolder'] = 'True'
def create_config() -> None:
"""
Creates the yaml config in the current directory with the filename pluGET_config.yml
"""
# this is the whole yaml code because of weird formating indention is not possible
configuration = """\
#
# Configuration File for pluGET
# https://www.github.com/Neocky/pluGET
#
config['Local - This Machine'] = {}
config['Local - This Machine']['PathToPluginFolder'] = 'C:/Users/USER/Desktop/plugins'
config['Local - This Machine'][';'] = 'For a different folder to store the updated plugins change to (True/False) and the path below'
config['Local - This Machine']['SeperateDownloadPath'] = 'False'
config['Local - This Machine']['PathToSeperateDownloadPath'] = 'C:/Users/USER/Desktop/plugins'
# What should be used for the connection (local, sftp, ftp)
Connection: local
config['SFTP - Remote Server'] = {}
config['SFTP - Remote Server']['Server'] = '0.0.0.0'
config['SFTP - Remote Server']['Username'] = 'user'
config['SFTP - Remote Server']['Password'] = 'password'
config['SFTP - Remote Server'][';'] = 'If a different Port for SFTP needs to be used (Works only for SFTP)'
config['SFTP - Remote Server']['SFTPPort'] = '22'
config['SFTP - Remote Server'][';_'] = 'Change the path below if the plugin folder path is different on the SFTP/FTP server (Change only if you know what you are doing)'
config['SFTP - Remote Server']['PluginFolderOnServer'] = '/plugins'
config['SFTP - Remote Server'][';__'] = 'If you want to use FTP instead of SFTP change to (False) else use (True)'
config['SFTP - Remote Server']['USE_SFTP'] = 'True'
config['SFTP - Remote Server'][';___'] = 'For a different folder to store the updated plugins (Only with the update command!) change to (True/False) and the path below'
config['SFTP - Remote Server']['SeperateDownloadPath'] = 'False'
config['SFTP - Remote Server']['PathToSeperateDownloadPath'] = '/plugins'
Local:
PathToPluginFolder: C:/Users/USER/Desktop/plugins
# If a different folder should be used to store the updated plugins change to (True/False) and the path below
SeperateDownloadPath : False
PathToSeperateDownloadPath: C:/Users/USER/Desktop/plugins
Remote:
Server: 0.0.0.0
Username: user
Password: password
# If a different Port for SFTP/FTP will be used
SFTP_Port: 22
FTP_Port: 21
# If a different folder should be used to store the updated plugins change to (True/False) and the path below
SeperateDownloadPath : False
PathToSeperateDownloadPath: /plugins/updated
# Change the path below if the plugin folder path is different on the SFTP/FTP server (Change only if you know what you are doing)
PluginFolderOnServer: /plugins
"""
# load ruamel.yaml to get the # commands right in the yaml code
yaml = ruamel.yaml.YAML()
code = yaml.load(configuration)
with open("pluGET_config.yaml", "w") as config_file:
yaml.dump(code, config_file)
config_file_path = os.path.abspath("pluGET_config.yaml")
print(f"Path of config file: {config_file_path}")
print("Config created. Edit config before executing again!")
input("Press any key + enter to exit...")
sys.exit()
with open('config.ini', 'w') as configfile:
config.write(configfile)
def validate_config() -> None:
"""
Validates the config variables after config class is loaded and exit if error is detected and print error
"""
accepted_values = [
("local", "sftp", "ftp")
]
# exit afterwards if there is an error in config
exit_afterwards = False
config = config_value()
# rich console for nice colors
console = Console()
if config.connection not in accepted_values[0]:
console.print(f"Error in Config! Accepted values for key 'Connection' are {accepted_values[0]}",
style="bright_red")
exit_afterwards = True
if exit_afterwards:
sys.exit()

View File

@ -1,110 +1,187 @@
import os
import sys
import ftplib
import stat
import re
from utils.consoleoutput import oColors
from handlers.handle_config import configurationValues
from src.utils.console_output import rich_print_error
from src.handlers.handle_config import config_value
def createFTPConnection():
configValues = configurationValues()
ftp = ftplib.FTP(configValues.sftp_server, user=configValues.sftp_user, \
passwd=configValues.sftp_password)
def ftp_create_connection():
"""
Creates a connection to the ftp server with the given values in the config
:returns: ftp connection type
"""
config_values = config_value()
try:
ftp = ftplib.FTP()
ftp.connect(config_values.server, config_values.ftp_port)
ftp.login(config_values.username, config_values.password)
return ftp
except UnboundLocalError:
print(oColors.brightRed + "[FTP]: Check your config.ini!" + oColors.standardWhite)
print(oColors.brightRed + "Exiting program..." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: Check your config file!")
rich_print_error("Exiting program...")
sys.exit()
def ftp_showPlugins(ftp):
configValues = configurationValues()
ftp.cwd(configValues.sftp_folderPath)
def ftp_show_plugins(ftp) -> None:
"""
Prints all plugins in the plugin folder
:param ftp: ftp connection
:returns: None
"""
config_values = config_value()
ftp.cwd(config_values.remote_plugin_folder_on_server)
for attr in ftp.dir():
print(attr.filename, attr)
return None
def ftp_upload_file(ftp, itemPath):
configValues = configurationValues()
if configValues.sftp_seperateDownloadPath is True:
uploadFolderPath = configValues.sftp_pathToSeperateDownloadPath
def ftp_upload_file(ftp, path_item) -> None:
"""
Uploads a file to the ftp server
:param ftp: ftp connection
:param path_item: Name of the item which should be uploaded
:returns: None
"""
config_values = config_value()
if config_values.remote_seperate_download_path is True:
path_upload_folder = config_values.remote_path_to_seperate_download_path
else:
uploadFolderPath = configValues.sftp_folderPath
path_upload_folder = config_values.remote_plugin_folder_on_server
try:
ftp.cwd(uploadFolderPath)
itemPath = os.path.relpath(itemPath, 'TempSFTPFolder/')
itemPath = str(itemPath)
currentDirectory = os.getcwd()
ftp.cwd(path_upload_folder)
path_item = os.path.relpath(path_item, 'TempSFTPFolder/')
path_item = str(path_item)
current_directory = os.getcwd()
os.chdir('TempSFTPFolder')
with open (itemPath, 'rb') as plugin_file:
ftp.storbinary('STOR '+ str(itemPath), plugin_file)
with open (path_item, 'rb') as plugin_file:
ftp.storbinary('STOR '+ str(path_item), plugin_file)
except FileNotFoundError:
print(oColors.brightRed + "[FTP]: The 'plugins' folder couldn*t be found on the remote host!" + oColors.standardWhite)
print(oColors.brightRed + "[FTP]: Aborting uploading." + oColors.standardWhite)
os.chdir(currentDirectory)
rich_print_error("Error: [FTP]: The 'plugins' folder couldn't be found on the remote host!")
rich_print_error("Error: [FTP]: Aborting uploading.")
os.chdir(current_directory)
ftp.close()
return None
def ftp_upload_server_jar(ftp, itemPath):
def ftp_upload_server_jar(ftp, path_item) -> None:
"""
Uploads a serverjar to the root folder of the ftp host
:param ftp: ftp connection
:param path_item: Name of the file which should be uploaded
:returns: None
"""
try:
ftp.cwd('.')
itemPath = os.path.relpath(itemPath, 'TempSFTPFolder/')
itemPath = str(itemPath)
currentDirectory = os.getcwd()
path_item = os.path.relpath(path_item, 'TempSFTPFolder/')
path_item = str(path_item)
current_directory = os.getcwd()
os.chdir('TempSFTPFolder')
with open (itemPath, 'rb') as server_jar:
ftp.storbinary('STOR '+ str(itemPath), server_jar)
with open (path_item, 'rb') as server_jar:
ftp.storbinary('STOR '+ str(path_item), server_jar)
except FileNotFoundError:
print(oColors.brightRed + "[FTP]: The 'root' folder couldn*t be found on the remote host!" + oColors.standardWhite)
print(oColors.brightRed + "[FTP]: Aborting uploading." + oColors.standardWhite)
os.chdir(currentDirectory)
rich_print_error("Error: [FTP]: The 'root' folder couldn't be found on the remote host!")
rich_print_error("Error: [FTP]: Aborting uploading.")
os.chdir(current_directory)
ftp.close()
return None
def ftp_listAll(ftp):
configValues = configurationValues()
def ftp_list_all(ftp):
"""
Returns a list with all installed plugins in the plugin folder of the ftp host
:param ftp: ftp connection
:returns: List of all plugins in plugin folder
"""
config_values = config_value()
try:
ftp.cwd(configValues.sftp_folderPath)
installedPlugins = ftp.nlst()
ftp.cwd(config_values.remote_plugin_folder_on_server)
installed_plugins = ftp.nlst()
except FileNotFoundError:
print(oColors.brightRed + "[FTP]: The 'plugins' folder couldn*t be found on the remote host!" + oColors.standardWhite)
rich_print_error("Error: [FTP]: The 'plugins' folder couldn't be found on the remote host!")
try:
return installedPlugins
return installed_plugins
except UnboundLocalError:
print(oColors.brightRed + "[FTP]: No plugins were found." + oColors.standardWhite)
rich_print_error("Error: [FTP]: No plugins were found.")
def ftp_listFilesInServerRoot(ftp):
def ftp_list_files_in_server_root(ftp):
"""
Returns a list with all files in the root folder of the ftp host
:param ftp: ftp connection
:returns: List of all files in root folder
"""
try:
ftp.cwd('.')
filesInServerRoot = ftp.nlst()
except FileNotFoundError:
print(oColors.brightRed + "[FTP]: The 'root' folder couldn*t be found on the remote host!" + oColors.standardWhite)
rich_print_error("Error: [FTP]: The 'root' folder couldn't be found on the remote host!")
try:
return filesInServerRoot
except UnboundLocalError:
print(oColors.brightRed + "[FTP]: No Serverjar was found." + oColors.standardWhite)
rich_print_error("Error: [FTP]: No Serverjar was found.")
def ftp_downloadFile(ftp, downloadPath, fileToDownload):
configValues = configurationValues()
ftp.cwd(configValues.sftp_folderPath)
filedata = open(downloadPath,'wb')
ftp.retrbinary('RETR '+fileToDownload, filedata.write)
def ftp_download_file(ftp, path_download, file) -> None:
"""
Download a file of the ftp server
:param ftp: ftp connection
:param path_download: Path to save downloaded file to
:param file: File to download
:returns None
"""
config_values = config_value()
ftp.cwd(config_values.remote_plugin_folder_on_server)
filedata = open(path_download,'wb')
ftp.retrbinary('RETR '+file, filedata.write)
filedata.close()
ftp.quit()
return None
def ftp_validateFileAttributes(ftp, pluginPath):
pluginFTPAttribute = ftp.lstat(pluginPath)
if stat.S_ISDIR(pluginFTPAttribute.st_mode):
return False
elif re.search(r'.jar$', pluginPath):
def ftp_is_file(ftp, plugin_path) -> bool:
"""
Check if file on ftp host is a file and not a directory
:param ftp: ftp connection
:param plugin_path
:returns: True if file is a file and not a directory
"""
if ftp.nlst(plugin_path) == [plugin_path]:
return True
else:
return False
def ftp_validate_file_attributes(ftp, plugin_path) -> bool:
"""
Check if a file is a legitimate plugin file
:param ftp: ftp connection
:param plugin_path: Path of file to check
:returns: If file is a plugin file or not
"""
if ftp_is_file(ftp, plugin_path) is False:
return False
if re.search(r'.jar$', plugin_path):
return True
else:
return False

View File

@ -1,105 +1,128 @@
import sys
""""
Handles the input through the pluGET command line
"""
from utils.consoleoutput import oColors
from utils.utilities import getHelp, getCommandHelp
from handlers.handle_config import configurationValues
from plugin.plugin_downloader import searchPackage, getSpecificPackage
from plugin.plugin_updatechecker import updateInstalledPackage, checkInstalledPackage
from plugin.plugin_remover import removePlugin
from serverjar.serverjar_checker import checkInstalledServerjar, updateServerjar
from serverjar.serverjar_paper import papermc_downloader
from src.utils.console_output import rich_print_error
from src.utils.utilities import get_command_help
from src.plugin.plugin_remover import delete_plugin
from src.plugin.plugin_downloader import get_specific_plugin_spiget, search_specific_plugin_spiget
from src.plugin.plugin_updatechecker import check_installed_plugins, update_installed_plugins
from src.serverjar.serverjar_updatechecker import \
check_update_available_installed_server_jar, update_installed_server_jar
from src.serverjar.serverjar_paper_velocity_waterfall import serverjar_papermc_update
from src.serverjar.serverjar_purpur import serverjar_purpur_update
def createInputLists():
global COMMANDLIST
COMMANDLIST = [
'get',
'update',
'check',
'search',
'exit',
'help',
'remove',
'get-paper'
]
global INPUTSELECTEDOBJECT
INPUTSELECTEDOBJECT = [
'all',
'*'
]
# check
# update
# get
# get-paper
# get-waterfall
# get-velocity
# get-purpur
# exit
# remove
# search
# help
def handleInput(inputCommand, inputSelectedObject, inputParams):
configValues = configurationValues()
def handle_input(
input_command : str=None,
input_selected_object : str=None,
input_parameter : str=None,
no_confirmation : bool=False,
arguments_from_console : bool=False
) -> None:
"""
Manages the correct function calling from the given input
:param input_command: Command of main function
:param input_selected_object: Name of plugin/serverjar
:param: input_parameter: Optional parameters
:param no_confirmation: If plugins should be updated without no confirmation message
:param arguments_from_console: If arguments were given on script call
:returns None:
"""
while True:
if inputCommand == 'get':
if inputSelectedObject.isdigit():
if not configValues.localPluginFolder:
if configValues.sftp_seperateDownloadPath is True:
pluginPath = configValues.sftp_pathToSeperateDownloadPath
else:
pluginPath = configValues.sftp_folderPath
getSpecificPackage(inputSelectedObject, pluginPath, inputParams)
break
else:
if configValues.seperateDownloadPath is True:
pluginPath = configValues.pathToSeperateDownloadPath
else:
pluginPath = configValues.pathToPluginFolder
getSpecificPackage(inputSelectedObject, pluginPath, inputParams)
break
else:
searchPackage(inputSelectedObject)
break
if inputCommand == 'update':
if inputSelectedObject == 'serverjar':
updateServerjar(inputParams)
else:
updateInstalledPackage(inputSelectedObject)
break
if inputCommand == 'check':
if inputSelectedObject == 'serverjar':
checkInstalledServerjar()
else:
checkInstalledPackage(inputSelectedObject, inputParams)
break
if inputCommand == 'search':
searchPackage(inputSelectedObject)
break
if inputCommand == 'exit':
sys.exit()
if inputCommand == 'help':
if inputSelectedObject == 'command' or inputSelectedObject == 'commands':
getCommandHelp(inputParams)
else:
getHelp()
break
if inputCommand == 'remove':
removePlugin(inputSelectedObject)
break
if inputCommand == 'get-paper':
papermc_downloader(inputSelectedObject, inputParams)
break
else:
print(oColors.brightRed + "Error: Command not found. Please try again. :(" + oColors.standardWhite)
print(oColors.brightRed + "Use: '" + oColors.standardWhite +"help command" + oColors.brightRed +"' to get all available commands" + oColors.standardWhite)
getInput()
getInput()
# when arguemnts were not passed from console ask for input
if arguments_from_console is False:
try:
input_command, input_selected_object, input_parameter = get_input()
except TypeError:
# KeyboardInterrupt was triggered and None was returned so exit
return
match input_command:
case "get":
match input_selected_object.isdigit():
case True:
get_specific_plugin_spiget(input_selected_object, input_parameter)
case _:
search_specific_plugin_spiget(input_selected_object)
case "get-paper":
serverjar_papermc_update(input_selected_object, input_parameter, None, "paper")
case "get-velocity":
serverjar_papermc_update(input_selected_object, input_parameter, None, "velocity")
case "get-waterfall":
serverjar_papermc_update(input_selected_object, input_parameter, None, "waterfall")
case "get-purpur":
serverjar_purpur_update(input_selected_object, input_parameter, None)
case "update":
match input_selected_object:
case "serverjar":
update_installed_server_jar(input_parameter)
case _:
update_installed_plugins(input_selected_object, no_confirmation)
case "check":
match input_selected_object:
case "serverjar":
check_update_available_installed_server_jar()
case _:
check_installed_plugins(input_selected_object, input_parameter)
case "search":
search_specific_plugin_spiget(input_selected_object)
case "remove":
delete_plugin(input_selected_object)
case "help":
get_command_help(input_selected_object)
case "exit":
return
case _:
rich_print_error("Error: Command not found. Please try again. :(")
rich_print_error("Use [bright_blue]'help all' [bright_red]to get a list of all available commands.")
# return to break out of while loop if pluGET was started with arguments from console
if arguments_from_console:
return None
def getInput():
inputCommand = None
def get_input() -> str:
"""
Gets command line input and calls the handle input function
:returns: Main command to execute
:returns: Selected Object to work with
:returns: Optional parameter
"""
input_command = None
print("\n'STRG + C' to exit")
while True:
try:
inputCommand, inputSelectedObject, *inputParams = input("pluGET >> ").split()
input_command, input_selected_object, *input_parameter = input("pluGET >> ").split()
break
except ValueError:
if inputCommand == None:
if input_command == None:
# request input again if no input was given or not enough
continue
else:
print(oColors.brightRed + "Wrong input! Use: > 'command' 'selectedObject' [optionalParams]" + oColors.standardWhite)
print(oColors.brightRed + "Use: '" + oColors.standardWhite +"help command" + oColors.brightRed +"' to get all available commands" + oColors.standardWhite)
rich_print_error("Wrong input! Use: > 'command' 'selectedObject' [optionalParams]")
rich_print_error("Use: [bright_blue]'help all' [bright_red]to get a list of all available commands.")
except KeyboardInterrupt:
sys.exit()
inputParams = inputParams[0] if inputParams else None
handleInput(inputCommand, inputSelectedObject, inputParams)
return
input_parameter = input_parameter[0] if input_parameter else None
return input_command, input_selected_object, input_parameter

View File

@ -5,103 +5,164 @@ import paramiko
import stat
import re
from utils.consoleoutput import oColors
from handlers.handle_config import configurationValues
from src.utils.console_output import rich_print_error
from src.handlers.handle_config import config_value
def createSFTPConnection():
configValues = configurationValues()
def sftp_create_connection():
"""
Creates a sftp connection with the given values in the config file
:returns: SFTP connection type
"""
config_values = config_value()
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None # TODO fix this
try:
sftp = pysftp.Connection(configValues.sftp_server, username=configValues.sftp_user, \
password=configValues.sftp_password, port=configValues.sftp_port, cnopts=cnopts)
sftp = pysftp.Connection(config_values.server, username=config_values.username, \
password=config_values.password, port=config_values.sftp_port, cnopts=cnopts)
except paramiko.ssh_exception.AuthenticationException:
print(oColors.brightRed + "[SFTP]: Wrong Username/Password" + oColors.standardWhite)
rich_print_error("Error: [SFTP]: Wrong Username/Password")
except paramiko.ssh_exception.SSHException:
print(oColors.brightRed + "[SFTP]: The SFTP server isn't available." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: The SFTP server isn't available.")
try:
return sftp
except UnboundLocalError:
print(oColors.brightRed + "[SFTP]: Check your config.ini!" + oColors.standardWhite)
print(oColors.brightRed + "Exiting program..." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: Check your config file!")
rich_print_error("Exiting program...")
sys.exit()
def sftp_showPlugins(sftp):
configValues = configurationValues()
sftp.cd(configValues.sftp_folderPath)
def sftp_show_plugins(sftp) -> None:
"""
Prints all plugins in the sftp folder
:param sftp: sftp connection
:returns: None
"""
config_values = config_value()
sftp.cd(config_values.remote_plugin_folder_on_server)
for attr in sftp.listdir_attr():
print(attr.filename, attr)
def sftp_upload_file(sftp, itemPath):
configValues = configurationValues()
if configValues.sftp_seperateDownloadPath is True:
uploadFolderPath = configValues.sftp_pathToSeperateDownloadPath
else:
uploadFolderPath = configValues.sftp_folderPath
try:
sftp.chdir(uploadFolderPath)
sftp.put(itemPath)
except FileNotFoundError:
print(oColors.brightRed + "[SFTP]: The 'plugins' folder couldn*t be found on the remote host!" + oColors.standardWhite)
print(oColors.brightRed + "[SFTP]: Aborting uploading." + oColors.standardWhite)
sftp.close()
return None
def sftp_upload_server_jar(sftp, itemPath):
def sftp_upload_file(sftp, path_item) -> None:
"""
Uploads a file to the set folder from the config file
:param sftp: sftp connection
:param path_item: The upload path with the item name
:returns: None
"""
config_values = config_value()
if config_values.remote_seperate_download_path is True:
path_upload_folder = config_values.remote_path_to_seperate_download_path
else:
path_upload_folder = config_values.remote_plugin_folder_on_server
try:
sftp.chdir(path_upload_folder)
sftp.put(path_item)
except FileNotFoundError:
rich_print_error("Error: [SFTP]: The 'plugins' folder couldn't be found on the remote host!")
rich_print_error("Error: [SFTP]: Aborting uploading.")
sftp.close()
return None
def sftp_upload_server_jar(sftp, path_item) -> None:
"""
Uploads the server jar to the root folder
:param sftp: sftp connection
:param path_item: The upload path with the item name
:returns: None
"""
try:
sftp.chdir('.')
sftp.put(itemPath)
sftp.put(path_item)
except FileNotFoundError:
print(oColors.brightRed + "[SFTP]: The 'root' folder couldn*t be found on the remote host!" + oColors.standardWhite)
print(oColors.brightRed + "[SFTP]: Aborting uploading." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: The 'root' folder couldn't be found on the remote host!")
rich_print_error("Error: [SFTP]: Aborting uploading.")
sftp.close()
return None
def sftp_listAll(sftp):
configValues = configurationValues()
def sftp_list_all(sftp):
"""
List all plugins in the 'plugins' folder on the sftp host
:param sftp: sftp connection
:return: List of plugins in plugin folder
"""
config_values = config_value()
try:
sftp.chdir(configValues.sftp_folderPath)
installedPlugins = sftp.listdir()
sftp.chdir(config_values.remote_plugin_folder_on_server)
installed_plugins = sftp.listdir()
except FileNotFoundError:
print(oColors.brightRed + "[SFTP]: The 'plugins' folder couldn*t be found on the remote host!" + oColors.standardWhite)
rich_print_error("Error: [SFTP]: The 'plugins' folder couldn't be found on the remote host!")
try:
return installedPlugins
return installed_plugins
except UnboundLocalError:
print(oColors.brightRed + "[SFTP]: No plugins were found." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: No plugins were found.")
def sftp_listFilesInServerRoot(sftp):
def sftp_list_files_in_server_root(sftp):
"""
List all files in the root folder on the sftp host
:param sftp: sftp connection
:returns: List of files in root folder
"""
try:
filesInServerRoot = sftp.listdir()
files_in_server_root = sftp.listdir()
except FileNotFoundError:
print(oColors.brightRed + "[SFTP]: The 'root' folder couldn*t be found on the remote host!" + oColors.standardWhite)
rich_print_error("Error: [SFTP]: The 'root' folder couldn't be found on the remote host!")
try:
return filesInServerRoot
return files_in_server_root
except UnboundLocalError:
print(oColors.brightRed + "[SFTP]: No Serverjar was found." + oColors.standardWhite)
rich_print_error("Error: [SFTP]: No Serverjar was found.")
def sftp_downloadFile(sftp, downloadPath, fileToDownload):
configValues = configurationValues()
sftp.cwd(configValues.sftp_folderPath)
currentDirectory = os.getcwd()
def sftp_download_file(sftp, file) -> None:
"""
Downloads a plugin file from the sftp host to a temporary folder
:param sftp: sftp connection
:param file: Filename of plugin
:returns: None
"""
config_values = config_value()
sftp.cwd(config_values.remote_plugin_folder_on_server)
current_directory = os.getcwd()
os.chdir('TempSFTPFolder')
sftp.get(fileToDownload)
sftp.get(file)
sftp.close()
os.chdir(currentDirectory)
os.chdir(current_directory)
return None
def sftp_validateFileAttributes(sftp, pluginPath):
pluginSFTPAttribute = sftp.lstat(pluginPath)
if stat.S_ISDIR(pluginSFTPAttribute.st_mode):
def sftp_validate_file_attributes(sftp, plugin_path) -> bool:
"""
Check if the file is a legitimate plugin file
:param sftp: sftp connection
param plugin_path: Path of the single plugin file
:returns: If file is a plugin file or not
"""
plugin_sftp_attribute = sftp.lstat(plugin_path)
if stat.S_ISDIR(plugin_sftp_attribute.st_mode):
return False
elif re.search(r'.jar$', pluginPath):
elif re.search(r'.jar$', plugin_path):
return True
else:
return False

View File

@ -1,148 +1,245 @@
"""
File and functions which handle the download of the specific plugins
"""
import re
import urllib.request
from urllib.error import HTTPError
from pathlib import Path
import requests
from utils.consoleoutput import oColors
from utils.web_request import doAPIRequest
from utils.utilities import createTempPluginFolder, deleteTempPluginFolder, calculateFileSizeKb, calculateFileSizeMb
from handlers.handle_config import configurationValues
from handlers.handle_sftp import sftp_upload_file, createSFTPConnection
from handlers.handle_ftp import ftp_upload_file, createFTPConnection
from rich.table import Table
from rich.console import Console
from rich.progress import Progress
from src.utils.utilities import convert_file_size_down, remove_temp_plugin_folder, create_temp_plugin_folder
from src.utils.utilities import api_do_request
from src.utils.console_output import rich_print_error
from src.handlers.handle_config import config_value
from src.handlers.handle_sftp import sftp_create_connection, sftp_upload_file
from src.handlers.handle_ftp import ftp_create_connection, ftp_upload_file
def handleRegexPackageName(packageNameFull):
packageNameFull2 = packageNameFull
def handle_regex_plugin_name(full_plugin_name) -> str:
"""
Return the plugin name after trimming clutter from name with regex operations
"""
# trims the part of the package that has for example "[1.1 Off]" in it
unwantedpackageName = re.search(r'(^\[+[a-zA-Z0-9\s\W*\.*\-*\+*\%*\,]*\]+)', packageNameFull)
unwantedpackageNamematch = bool(unwantedpackageName)
if unwantedpackageNamematch:
unwantedpackageNameString = unwantedpackageName.group()
packageNameFull2 = packageNameFull.replace(unwantedpackageNameString, '')
# gets the real packagename "word1 & word2" is not supported only gets word 1
packageName = re.search(r'([a-zA-Z]\d*)+(\s?\-*\_*[a-zA-Z]\d*\+*\-*\'*)+', packageNameFull2)
packageNameFullString = packageName.group()
packageNameOnly = packageNameFullString.replace(' ', '')
return packageNameOnly
unwanted_plugin_name = re.search(r'(^\[+[a-zA-Z0-9\s\W*\.*\-*\+*\%*\,]*\]+)', full_plugin_name)
if bool(unwanted_plugin_name):
unwanted_plugin_name_string = unwanted_plugin_name.group()
full_plugin_name = full_plugin_name.replace(unwanted_plugin_name_string, '')
# gets the real plugin_name "word1 & word2" is not supported only gets word1
plugin_name = re.search(r'([a-zA-Z]\d*)+(\s?\-*\_*[a-zA-Z]\d*\+*\-*\'*)+', full_plugin_name)
try:
plugin_name_full_string = plugin_name.group()
found_plugin_name = plugin_name_full_string.replace(' ', '')
except AttributeError:
found_plugin_name = unwanted_plugin_name_string
return found_plugin_name
def getVersionID(packageId, packageVersion):
if packageVersion == None or packageVersion == 'latest':
url = f"https://api.spiget.org/v2/resources/{packageId}/versions/latest"
response = doAPIRequest(url)
versionId = response["id"]
return versionId
def get_version_id_spiget(plugin_id, plugin_version) -> str:
"""
Returns the version id of the plugin
"""
if plugin_version == None or plugin_version == 'latest':
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/latest"
response = api_do_request(url)
if response == None:
return None
version_id = response["id"]
return version_id
url = f"https://api.spiget.org/v2/resources/{packageId}/versions?size=100&sort=-name"
versionList = doAPIRequest(url)
for packages in versionList:
packageUpdate = packages["name"]
versionId = packages["id"]
if packageUpdate == packageVersion:
return versionId
return versionList[0]["id"]
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions?size=100&sort=-name"
version_list = api_do_request(url)
if version_list == None:
return None
for plugins in version_list:
plugin_update = plugins["name"]
version_id = plugins["id"]
if plugin_update == plugin_version:
return version_id
return version_list[0]["id"]
def getVersionName(packageId, versionId):
url = f"https://api.spiget.org/v2/resources/{packageId}/versions/{versionId}"
response = doAPIRequest(url)
versionName = response["name"]
return versionName
def get_version_name_spiget(plugin_id, plugin_version_id) -> str:
"""
Returns the name of a specific version
"""
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/{plugin_version_id}"
response = api_do_request(url)
if response == None:
return None
version_name = response["name"]
return version_name
def searchPackage(resourceName):
configValues = configurationValues()
url = f"https://api.spiget.org/v2/search/resources/{resourceName}?field=name&sort=-downloads"
packageName = doAPIRequest(url)
i = 1
print(oColors.brightBlack + f"Searching: {resourceName}" + oColors.standardWhite)
print("┌─────┬─────────────────────────────┬───────────┬──────────────────────────────────────────────────────────────────────┐")
print("│ No. │ Name │ Downloads │ Description │")
print("└─────┴─────────────────────────────┴───────────┴──────────────────────────────────────────────────────────────────────┘")
for resource in packageName:
pName = resource["name"]
newName = handleRegexPackageName(pName)
pTag = resource["tag"]
pDownloads = resource["downloads"]
print(f" [{i}]".rjust(6), end='')
print(" ", end='')
print(f"{newName}".ljust(30), end='')
print(f"{pDownloads}".rjust(9), end='')
print(" ", end='')
print(f"{pTag}".ljust(120))
i = i + 1
def get_download_path(config_values) -> str:
"""
Reads the config and gets the path of the plugin folder
"""
match (config_values.connection):
case "local":
match (config_values.local_seperate_download_path):
case True:
return config_values.local_path_to_seperate_download_path
case _:
return config_values.path_to_plugin_folder
case _:
match (config_values.remote_seperate_download_path):
case True:
return config_values.remote_path_to_seperate_download_path
case _:
return config_values.remote_plugin_folder_on_server
resourceSelected = int(input("Select your wanted resource (No.)(0 to exit): "))
if resourceSelected != 0:
resourceSelected = resourceSelected - 1
resourceId = packageName[resourceSelected]["id"]
if not configValues.localPluginFolder:
if configValues.sftp_seperateDownloadPath is True:
pluginDownloadPath = configValues.sftp_pathToSeperateDownloadPath
else:
pluginDownloadPath = configValues.sftp_folderPath
else:
if configValues.seperateDownloadPath is True:
pluginDownloadPath = configValues.pathToSeperateDownloadPath
else:
pluginDownloadPath = configValues.pathToPluginFolder
def download_specific_plugin_version_spiget(plugin_id, download_path, version_id="latest") -> None:
"""
Download a specific plugin
"""
config_values = config_value()
if version_id != "latest" and version_id != None:
#url = f"https://spigotmc.org/resources/{plugin_id}/download?version={versionID}"
rich_print_error("Sorry but specific version downloads aren't supported because of cloudflare protection. :(")
rich_print_error("Reverting to latest version.")
#throws 403 forbidden error...cloudflare :(
#url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/latest/download"
url = f"https://api.spiget.org/v2/resources/{plugin_id}/download"
# use rich Progress() to create progress bar
with Progress(transient=True) as progress:
header = {'user-agent': 'pluGET/1.0'}
r = requests.get(url, headers=header, stream=True)
try:
getSpecificPackage(resourceId, pluginDownloadPath)
except HTTPError as err:
print(oColors.brightRed + f"Error: {err.code} - {err.reason}" + oColors.standardWhite)
file_size = int(r.headers.get('content-length'))
# create progress bar
download_task = progress.add_task(" [cyan]Downloading...", total=file_size)
except TypeError:
# Content-lenght returned nothing
file_size = 0
with open(download_path, 'wb') as f:
# split downloaded data in chunks of 32768
for data in r.iter_content(chunk_size=32768):
f.write(data)
# don't show progress bar if no content-length was returned
if file_size == 0:
continue
progress.update(download_task, advance=len(data))
#f.flush()
def downloadSpecificVersion(resourceId, downloadPath, versionID='latest'):
configValues = configurationValues()
if versionID != 'latest':
#url = f"https://spigotmc.org/resources/{resourceId}/download?version={versionID}"
print(oColors.brightRed + "Sorry but specific version downloads aren't supported because of cloudflare protection. :(" + oColors.standardWhite)
print(oColors.brightRed + "Reverting to latest version." + oColors.standardWhite)
url = f"https://api.spiget.org/v2/resources/{resourceId}/download"
#url = f"https://api.spiget.org/v2/resources/{resourceId}/versions/latest/download" #throws 403 forbidden error...cloudflare :(
urrlib_opener = urllib.request.build_opener()
urrlib_opener.addheaders = [('User-agent', 'pluGET/1.0')]
urllib.request.install_opener(urrlib_opener)
remotefile = urllib.request.urlopen(url)
filesize = remotefile.info()['Content-Length']
urllib.request.urlretrieve(url, downloadPath)
filesize = int(filesize)
print(" ", end='')
if filesize >= 1000000:
filesizeData = calculateFileSizeMb(filesize)
print("Downloaded " + (str(filesizeData)).rjust(9) + f" MB here {downloadPath}")
# use rich console for nice colors
console = Console()
if file_size == 0:
console.print(
f" [not bold][bright_green]Downloaded[bright_magenta] file [cyan]→ [white]{download_path}"
)
elif file_size >= 1000000:
file_size_data = convert_file_size_down(convert_file_size_down(file_size))
console.print(" [not bold][bright_green]Downloaded[bright_magenta] " + (str(file_size_data)).rjust(9) + \
f" MB [cyan]→ [white]{download_path}")
else:
filesizeData = calculateFileSizeKb(filesize)
print("Downloaded " + (str(filesizeData)).rjust(9) + f" KB here {downloadPath}")
if not configValues.localPluginFolder:
if configValues.sftp_useSftp:
sftpSession = createSFTPConnection()
sftp_upload_file(sftpSession, downloadPath)
else:
ftpSession = createFTPConnection()
ftp_upload_file(ftpSession, downloadPath)
file_size_data = convert_file_size_down(file_size)
console.print(" [not bold][bright_green]Downloaded[bright_magenta] " + (str(file_size_data)).rjust(9) + \
f" KB [cyan]→ [white]{download_path}")
if config_values.connection == "sftp":
sftp_session = sftp_create_connection()
sftp_upload_file(sftp_session, download_path)
elif config_values.connection == "ftp":
ftp_session = ftp_create_connection()
ftp_upload_file(ftp_session, download_path)
return None
def getSpecificPackage(resourceId, downloadPath, inputPackageVersion='latest'):
configValues = configurationValues()
if configValues.localPluginFolder == False:
downloadPath = createTempPluginFolder()
url = f"https://api.spiget.org/v2/resources/{resourceId}"
packageDetails = doAPIRequest(url)
packageName = packageDetails["name"]
packageNameNew = handleRegexPackageName(packageName)
versionId = getVersionID(resourceId, inputPackageVersion)
packageVersion = getVersionName(resourceId, versionId)
packageDownloadName = f"{packageNameNew}-{packageVersion}.jar"
downloadPackagePath = Path(f"{downloadPath}/{packageDownloadName}")
if inputPackageVersion is None or inputPackageVersion == 'latest':
downloadSpecificVersion(resourceId=resourceId, downloadPath=downloadPackagePath)
def get_specific_plugin_spiget(plugin_id, plugin_version="latest") -> None:
"""
Gets the specific plugin and calls the download function
"""
config_values = config_value()
# use a temporary folder to store plugins until they are uploaded
if config_values.connection != "local":
download_path = create_temp_plugin_folder()
else:
downloadSpecificVersion(resourceId, downloadPackagePath, versionId)
download_path = get_download_path(config_values)
if not configValues.localPluginFolder:
deleteTempPluginFolder(downloadPath)
url = f"https://api.spiget.org/v2/resources/{plugin_id}"
plugin_details = api_do_request(url)
if plugin_details == None:
return None
try:
plugin_name = plugin_details["name"]
except KeyError:
# exit if plugin id couldn't be found
rich_print_error("Error: Plugin ID couldn't be found")
return None
plugin_name = handle_regex_plugin_name(plugin_name)
plugin_version_id = get_version_id_spiget(plugin_id, plugin_version)
plugin_version_name = get_version_name_spiget(plugin_id, plugin_version_id)
plugin_download_name = f"{plugin_name}-{plugin_version_name}.jar"
download_plugin_path = Path(f"{download_path}/{plugin_download_name}")
# if api requests weren't successfull stop function
if plugin_version_id == None or plugin_version_name == None:
rich_print_error("Error: Webrequest timed out")
return None
# set the plugin_version_id to None if a specific version wasn't given as parameter
if plugin_version == "latest" or plugin_version is None:
plugin_version_id = None
download_specific_plugin_version_spiget(plugin_id, download_plugin_path, plugin_version_id)
if config_values.connection != "local":
remove_temp_plugin_folder()
return None
def search_specific_plugin_spiget(plugin_name) -> None:
"""
Search for a name and return the top 10 results sorted for their download count
Then ask for input and download that plugin
"""
url= f"https://api.spiget.org/v2/search/resources/{plugin_name}?field=name&sort=-downloads"
plugin_search_results = api_do_request(url)
if plugin_search_results == None:
rich_print_error("Error: Webrequest wasn't successfull!")
return None
print(f"Searching for {plugin_name}...")
print(f"Found plugins:")
# create table with rich
rich_table = Table(box=None)
rich_table.add_column("No.", justify="right", style="cyan", no_wrap=True)
rich_table.add_column("Name", style="bright_magenta")
rich_table.add_column("Downloads", justify="right", style="bright_green")
rich_table.add_column("Description", justify="left", style="white")
# start counting at 1 for all my non-programming friends :)
i = 1
for found_plugin in plugin_search_results:
plugin_name = handle_regex_plugin_name(found_plugin["name"])
plugin_downloads = found_plugin["downloads"]
plugin_description = found_plugin["tag"]
rich_table.add_row(str(i), plugin_name, str(plugin_downloads), plugin_description)
i += 1
# print table from rich
rich_console = Console()
rich_console.print(rich_table)
try:
plugin_selected = input("Select your wanted resource (No.)(0 to exit): ")
except KeyboardInterrupt:
return None
if plugin_selected == "0":
return None
try:
plugin_selected = int(plugin_selected) - 1
plugin_selected_id = plugin_search_results[plugin_selected]["id"]
except ValueError:
rich_print_error("Error: Input wasn't a number! Please try again!")
return None
except IndexError:
rich_print_error("Error: Number was out of range! Please try again!")
return None
selected_plugin_name = handle_regex_plugin_name(plugin_search_results[plugin_selected]["name"])
rich_console.print(f"\n [not bold][bright_white]● [bright_magenta]{selected_plugin_name} [bright_green]latest")
get_specific_plugin_spiget(plugin_selected_id)

View File

@ -1,59 +1,56 @@
"""
Removes the specified plugin file from the ./plugins folder
"""
import os
import re
from pathlib import Path
from rich.console import Console
from utils.consoleoutput import oColors
from handlers.handle_config import configurationValues
from handlers.handle_sftp import createSFTPConnection, sftp_listAll
from handlers.handle_ftp import createFTPConnection, ftp_listAll
from plugin.plugin_updatechecker import getFileName, getFileVersion, getInstalledPlugin, createPluginList
from src.handlers.handle_config import config_value
from src.utils.console_output import rich_print_error
from src.handlers.handle_sftp import sftp_create_connection, sftp_list_all
from src.handlers.handle_ftp import ftp_create_connection, ftp_list_all
def removePlugin(pluginToRemove):
configValues = configurationValues()
createPluginList()
if not configValues.localPluginFolder:
if not configValues.sftp_useSftp:
ftp = createFTPConnection()
pluginList = ftp_listAll(ftp)
else:
sftp = createSFTPConnection()
pluginList = sftp_listAll(sftp)
else:
pluginList = os.listdir(configValues.pathToPluginFolder)
i = 0
try:
for plugin in pluginList:
try:
fileName = getFileName(plugin)
fileVersion = getFileVersion(plugin)
pluginId = getInstalledPlugin(fileName, fileVersion)
except TypeError:
continue
pluginIdStr = str(pluginId)
def delete_plugin(plugin_name: str) -> None:
"""
Deletes the specific plugin file
if pluginToRemove == pluginIdStr or re.search(pluginToRemove, fileName, re.IGNORECASE):
print(f"Removing: {fileName}")
if not configValues.localPluginFolder:
pluginPath = configValues.sftp_folderPath
pluginPath = f"{pluginPath}/{plugin}"
if not configValues.sftp_useSftp:
ftp = createFTPConnection()
ftp.delete(pluginPath)
else:
sftp = createSFTPConnection()
sftp.remove(pluginPath)
print(f"Removed: {fileName}")
i += 1
break
else:
pluginPath = configValues.pathToPluginFolder
pluginPath = Path(f"{pluginPath}/{plugin}")
:param plugin_name: Name of plugin file to delete
:returns: None
"""
config_values = config_value()
rich_console = Console()
match config_values.connection:
case "sftp":
connection = sftp_create_connection()
plugin_list = sftp_list_all()
case "ftp":
connection = ftp_create_connection()
plugin_list = ftp_list_all()
case "local":
plugin_list = os.listdir(config_values.path_to_plugin_folder)
for plugin_file in plugin_list:
# skip all other plugins
if not re.search(plugin_name, plugin_file, re.IGNORECASE):
continue
try:
match config_values.connection:
case "sftp":
plugin_path = f"{config_values.remote_plugin_folder_on_server}/{plugin_file}"
connection = sftp_create_connection()
connection.remove(plugin_path)
case "ftp":
plugin_path = f"{config_values.remote_plugin_folder_on_server}/{plugin_file}"
connection = ftp_create_connection()
connection.delete(plugin_path)
case "local":
pluginPath = Path(f"{config_values.path_to_plugin_folder}/{plugin_file}")
os.remove(pluginPath)
print(f"Removed: {fileName}")
i += 1
break
except TypeError:
print(oColors.brightRed + f"Aborted removing of: {pluginToRemove}." + oColors.standardWhite)
if i == 0:
print(oColors.brightRed + f"Couldn't remove plugin: {pluginToRemove}" + oColors.standardWhite)
rich_console.print(f"[not bold][bright_green]Successfully removed: [bright_magenta]{plugin_file}")
except:
rich_print_error(f"[not bold]Error: Couldn't remove [bright_magenta]{plugin_file}")
return None

File diff suppressed because it is too large Load Diff

View File

@ -1,114 +0,0 @@
import os
import sys
from urllib.error import HTTPError
from pathlib import Path
from handlers.handle_sftp import createSFTPConnection, sftp_listFilesInServerRoot
from handlers.handle_ftp import createFTPConnection, ftp_listFilesInServerRoot
from handlers.handle_config import configurationValues
from utils.consoleoutput import oColors
from serverjar.serverjar_paper import paperCheckForUpdate, papermc_downloader
def checkInstalledServerjar():
configValues = configurationValues()
if not configValues.localPluginFolder:
if not configValues.sftp_useSftp:
ftp = createFTPConnection()
serverRootList = ftp_listFilesInServerRoot(ftp)
else:
sftp = createSFTPConnection()
serverRootList = sftp_listFilesInServerRoot(sftp)
else:
serverRootList = os.path.dirname(configValues.pathToPluginFolder)
serverRootList = os.listdir(serverRootList)
installedServerjarFullName = None
try:
for files in serverRootList:
try:
if '.jar' in files:
installedServerjarFullName = files
break
except TypeError:
continue
except TypeError:
print(oColors.brightRed + "Serverjar couldn't be found." + oColors.standardWhite)
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
if installedServerjarFullName == None:
print(oColors.brightRed + "Serverjar couldn't be found." + oColors.standardWhite)
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
print(oColors.brightBlack + f"Checking: {installedServerjarFullName}" + oColors.standardWhite)
if 'paper' in installedServerjarFullName:
paperCheckForUpdate(installedServerjarFullName)
else:
print(oColors.brightRed + f"{installedServerjarFullName} isn't supported.")
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
def updateServerjar(serverJarBuild='latest'):
configValues = configurationValues()
try:
if serverJarBuild == None:
serverJarBuild = 'latest'
if not configValues.localPluginFolder:
sftp = createSFTPConnection()
serverRootPath = configValues.sftp_folderPath
serverRootPath = Path(str(serverRootPath).replace(r'/plugins', ''))
serverRootList = sftp_listFilesInServerRoot(sftp)
else:
serverRoot = os.path.dirname(configValues.pathToPluginFolder)
serverRootList = os.listdir(serverRoot)
serverRootPath = configValues.pathToPluginFolder
helpPath = Path('/plugins')
helpPathstr = str(helpPath)
serverRootPath = Path(str(serverRootPath).replace(helpPathstr, ''))
installedServerjarFullName = None
except FileNotFoundError:
print(oColors.brightRed + "Path couldn't be found!" + oColors.standardWhite)
print(oColors.brightRed + "Check your config!" + oColors.standardWhite)
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
try:
for files in serverRootList:
try:
if '.jar' in files:
installedServerjarFullName = files
break
except TypeError:
continue
except TypeError:
print(oColors.brightRed + "Serverjar couldn't be found." + oColors.standardWhite)
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
if installedServerjarFullName == None:
print(oColors.brightRed + "Serverjar couldn't be found." + oColors.standardWhite)
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
serverJarPath = Path(f"{serverRootPath}/{installedServerjarFullName}")
if 'paper' in installedServerjarFullName:
print(oColors.brightBlack + f"Updating Paper to build: {serverJarBuild}" + oColors.standardWhite)
try:
papermc_downloader(serverJarBuild, None, installedServerjarFullName)
if not configValues.localPluginFolder:
sftp.remove(serverJarPath)
else:
os.remove(serverJarPath)
except HTTPError as err:
print(oColors.brightRed + f"Error: {err.code} - {err.reason}" + oColors.standardWhite)
except FileNotFoundError:
print(oColors.brightRed + "Error: Old serverjar file coulnd't be deleted" + oColors.standardWhite)
else:
print(oColors.brightRed + f"{installedServerjarFullName} isn't supported.")
print(oColors.brightRed + "Aborting the process." + oColors.standardWhite)

View File

@ -1,218 +0,0 @@
import re
import urllib.request
from pathlib import Path
from rich.console import Console
from utils.consoleoutput import oColors
from utils.web_request import doAPIRequest
from handlers.handle_sftp import createSFTPConnection, sftp_upload_server_jar
from handlers.handle_ftp import createFTPConnection, ftp_upload_server_jar
from handlers.handle_config import configurationValues
from utils.utilities import createTempPluginFolder, deleteTempPluginFolder, calculateFileSizeMb
def getInstalledPaperMinecraftVersion(localPaperName):
if localPaperName is None:
return False
mcVersionFull = re.search(r'(\d*\.*\d)+', localPaperName)
try:
mcVersion = mcVersionFull.group()
except AttributeError:
mcVersion = mcVersionFull
return mcVersion
def getInstalledPaperVersion(localPaperName):
if localPaperName is None:
return False
paperBuildFull = re.search(r'([\d]*.jar)', localPaperName)
try:
paperBuild = paperBuildFull.group()
except AttributeError:
paperBuild = paperBuildFull
paperBuild = paperBuild.replace('.jar', '')
return paperBuild
def findVersionGroup(mcVersion):
versionGroups = ['1.17', '1.16', '1.15']
if mcVersion is None:
return False
for versionGroup in versionGroups:
url = f"https://papermc.io/api/v2/projects/paper/version_group/{versionGroup}/builds"
papermcdetails = doAPIRequest(url)
papermcVersionForMc = papermcdetails["versions"]
for versions in papermcVersionForMc:
if versions == mcVersion:
paperVersionGroup = versionGroup
return paperVersionGroup
if versionGroup == mcVersion:
paperVersionGroup = versionGroup
return paperVersionGroup
return False # Not found
def findBuildVersion(wantedPaperBuild):
versionGroups = ['1.17', '1.16', '1.15']
if wantedPaperBuild is None:
return False
for versionGroup in versionGroups:
url = f"https://papermc.io/api/v2/projects/paper/version_group/{versionGroup}/builds"
papermcdetails = doAPIRequest(url)
paperMcBuilds = papermcdetails["builds"]
for build in paperMcBuilds:
paperBuild = str(build["build"])
if paperBuild == wantedPaperBuild:
paperVersionGroup = build["version"]
return paperVersionGroup
return False # Not found
def findLatestBuild(paperVersionGroup):
if paperVersionGroup is None:
return False
url = f"https://papermc.io/api/v2/projects/paper/version_group/{paperVersionGroup}/builds"
papermcbuilds = doAPIRequest(url)
if "status" in papermcbuilds: # Checks if the API returns a status. This means that there was an error.
return False
latestPaperBuild = papermcbuilds["builds"][-1]["build"]
return latestPaperBuild
def findLatestBuildForVersion(mcVersion):
if mcVersion is None:
return False
url = f"https://papermc.io/api/v2/projects/paper/versions/{mcVersion}"
papermcbuilds = doAPIRequest(url)
latestPaperBuild = papermcbuilds["builds"][-1]
return latestPaperBuild
def versionBehind(installedPaperBuild, latestPaperBuild):
if installedPaperBuild is None or latestPaperBuild is None:
return False
installedPaperBuildint = int(installedPaperBuild)
latestPaperBuildint = int(latestPaperBuild)
versionsBehind = latestPaperBuildint - installedPaperBuildint
return versionsBehind
def getDownloadFileName(paperMcVersion, paperBuild):
if paperMcVersion is None or paperBuild is None:
return False
url = f"https://papermc.io/api/v2/projects/paper/versions/{paperMcVersion}/builds/{paperBuild}"
buildDetails = doAPIRequest(url)
downloadName = buildDetails["downloads"]["application"]["name"]
return downloadName
def paperCheckForUpdate(installedServerjarFullName):
mcVersion = getInstalledPaperMinecraftVersion(installedServerjarFullName)
# Report an error if getInstalledPaperMinecraftVersion encountered an issue.
if not mcVersion:
print(oColors.brightRed + "ERR: An error was encountered while detecting the server's Minecraft version." +
oColors.standardWhite)
return False
paperInstalledBuild = getInstalledPaperVersion(installedServerjarFullName)
# Report an error if getInstalledPaperVersion encountered an issue.
if not paperInstalledBuild:
print(oColors.brightRed + "ERR: An error was encountered while detecting the server's Paper version." +
oColors.standardWhite)
return False
versionGroup = findVersionGroup(mcVersion)
# Report an error if findVersionGroup encountered an issue.
if not versionGroup:
print(oColors.brightRed + "ERR: An error was encountered while fetching the server's version group." +
oColors.standardWhite)
return False
paperLatestBuild = findLatestBuild(versionGroup)
# Report an error if findLatestBuild encountered an issue.
if not paperLatestBuild:
print(oColors.brightRed + "ERR: An error was encountered while fetching the latest version of PaperMC." +
oColors.standardWhite)
return False # Not currently handled, but can be at a later date. Currently just stops the following from
# being printed.
paperVersionBehind = versionBehind(paperInstalledBuild, paperLatestBuild)
# Report an error if getInstalledPaperVersion encountered an issue.
if not paperVersionBehind:
print(oColors.brightRed + "ERR: An error was encountered while detecting how many versions behind you are. "
f"Will display as 'N/A'." + oColors.standardWhite)
paperVersionBehind = "N/A" # Sets paperVersionBehind to N/A while still letting the versionBehind check return
# False for error-handing reasons.
# Does not return false as versions behind doesn't break things. It is just helpful information.
# paperVersionBehind will just display as "N/A"
print("┌─────┬────────────────────────────────┬──────────────┬──────────────┐")
print("│ No. │ Name │ Installed V. │ Latest V. │")
print("└─────┴────────────────────────────────┴──────────────┴──────────────┘")
print(" [1]".rjust(6), end='')
print(" ", end='')
if paperVersionBehind != 0:
print(oColors.brightRed + "paper".ljust(33) + oColors.standardWhite, end='')
else:
print(oColors.brightGreen + "paper".ljust(33) + oColors.standardWhite, end='')
print(f"{paperInstalledBuild}".ljust(15), end='')
print(f"{paperLatestBuild}".ljust(15))
print(oColors.brightYellow + f"Versions behind: [{paperVersionBehind}]" + oColors.standardWhite)
# https://papermc.io/api/docs/swagger-ui/index.html?configUrl=/api/openapi/swagger-config#/
def papermc_downloader(paperBuild='latest', mcVersion=None, installedServerjarName=None):
configValues = configurationValues()
if configValues.localPluginFolder == False:
downloadPath = createTempPluginFolder()
else:
downloadPath = configValues.pathToPluginFolder
helpPath = Path('/plugins')
helpPathstr = str(helpPath)
downloadPath = Path(str(downloadPath).replace(helpPathstr, ''))
if mcVersion == None:
if paperBuild == 'latest':
mcVersion = '1.17.1'
else:
mcVersion = findBuildVersion(paperBuild)
if installedServerjarName != None:
mcVersion = getInstalledPaperMinecraftVersion(installedServerjarName)
if paperBuild == 'latest':
paperBuild = findLatestBuildForVersion(mcVersion)
try:
downloadFileName = getDownloadFileName(mcVersion, paperBuild)
except KeyError:
print(oColors.brightRed + f"This version wasn't found for {mcVersion}" + oColors.standardWhite)
print(oColors.brightRed + f"Reverting to latest version for {mcVersion}" + oColors.standardWhite)
paperBuild = findLatestBuildForVersion(mcVersion)
downloadFileName = getDownloadFileName(mcVersion, paperBuild)
downloadPackagePath = Path(f"{downloadPath}/{downloadFileName}")
if configValues.localPluginFolder == False:
downloadPath = createTempPluginFolder()
url = f"https://papermc.io/api/v2/projects/paper/versions/{mcVersion}/builds/{paperBuild}/downloads/{downloadFileName}"
remotefile = urllib.request.urlopen(url)
filesize = remotefile.info()['Content-Length']
print(f"Getting Paper {paperBuild} for {mcVersion}")
console = Console()
with console.status("Downloading...", spinner='line', spinner_style='bright_magenta') as status:
urllib.request.urlretrieve(url, downloadPackagePath)
filesizeData = calculateFileSizeMb(filesize)
print("Downloaded " + (str(filesizeData)).rjust(9) + f" MB here {downloadPackagePath}")
if not configValues.localPluginFolder:
if not configValues.sftp_useSftp:
ftpSession = createFTPConnection()
ftp_upload_server_jar(ftpSession, downloadPackagePath)
else:
sftpSession = createSFTPConnection()
sftp_upload_server_jar(sftpSession, downloadPackagePath)
deleteTempPluginFolder(downloadPath)
print(oColors.brightGreen + "Downloaded successfully " + oColors.standardWhite + f"Paper {paperBuild}" + \
oColors.brightGreen + " for " + oColors.standardWhite + f"{mcVersion}" + oColors.standardWhite)

View File

@ -0,0 +1,277 @@
"""
Handles the update checking and downloading of these serverjars:
Paper, Velocity, Waterfall
All are from the PaperMC Team and use the same api structure which is the reason these are together
"""
import re
import requests
from pathlib import Path
from rich.table import Table
from rich.console import Console
from rich.progress import Progress
from src.handlers.handle_config import config_value
from src.utils.console_output import rich_print_error
from src.handlers.handle_sftp import sftp_create_connection, sftp_upload_server_jar
from src.handlers.handle_ftp import ftp_create_connection, ftp_upload_server_jar
from src.utils.utilities import \
api_do_request, create_temp_plugin_folder, remove_temp_plugin_folder, convert_file_size_down
def get_installed_serverjar_version(file_server_jar_full_name) -> str:
"""
Gets the installed version of the installed serverjar
:param file_server_jar_full_name: Full file name fo the installed serverjar
:returns: Used serverjar version
"""
serverjar_version_full = re.search(r"([\d]*.jar)", file_server_jar_full_name)
try:
serverjar_version = serverjar_version_full.group()
except AttributeError:
serverjar_version = serverjar_version_full
serverjar_version = serverjar_version.replace('.jar', '')
return serverjar_version
def get_version_group(file_server_jar_full_name) -> str:
"""
Gets the version group which is used for the papermc api
:param mc_version: Version of Minecraft in use
:returns: Version group of api
"""
version_group = re.sub(r"-\d*.jar$", "", file_server_jar_full_name)
version_group = re.sub(r"^(\w*\-)", "", version_group)
return version_group
def find_latest_available_version(file_server_jar_full_name, version_group) -> int:
"""
Gets the latest available version of the installed serverjar version
:param version_group: Minecraft version group of the serverjar
:returns: Latest available version as int
"""
if "paper" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/paper/versions/{version_group}/builds"
elif "waterfall" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/waterfall/versions/{version_group}/builds"
elif "velocity" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/velocity/versions/{version_group}/builds"
versions = api_do_request(url)
if "status" in versions: # Checks if the API returns a status. This means that there was an error.
return None
latest_version = versions["builds"][-1]["build"]
return latest_version
def get_versions_behind(serverjar_version, latest_version) -> int:
"""
Gets the number diffference between the two versions
:param serverjar_version: Installed serverjar version
:param latest_version: Latest avaialable serverjar version
:returns: Number difference between the two versions
"""
versions_behind = int(latest_version) - int(serverjar_version)
return versions_behind
def get_papermc_download_file_name(mc_version, serverjar_version, file_server_jar_full_name) -> str:
"""
Gets the download name from the papermc api
:param mc_version: Minecraft version
:param serverjar_version: Version of the serverjar
:param file_server_jar_full_name: Serverjar name
:returns: Download name of the file
"""
if "paper" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/paper/versions/{mc_version}/builds/{serverjar_version}"
elif "waterfall" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/waterfall/versions/{mc_version}/builds/{serverjar_version}"
elif "velocity" in file_server_jar_full_name:
url = f"https://papermc.io/api/v2/projects/velocity/versions/{mc_version}/builds/{serverjar_version}"
build_details = api_do_request(url)
download_name = build_details["downloads"]["application"]["name"]
return download_name
def serverjar_papermc_check_update(file_server_jar_full_name) -> None:
"""
Checks the installed paper serverjar if an update is available
:param file_server_jar_full_name: Full name of the paper server jar file name
:returns: None
"""
serverjar_version = get_installed_serverjar_version(file_server_jar_full_name)
if serverjar_version == None:
rich_print_error("Error: An error occured while checking the installed serverjar version")
return None
version_group = get_version_group(file_server_jar_full_name)
if version_group == None:
rich_print_error(
"Error: An error occured while checking the installed version group of the installed serverjar"
)
return None
latest_version = find_latest_available_version(file_server_jar_full_name, version_group)
if latest_version == None:
rich_print_error("Error: An error occured while checking for the latest available version of the serverjar")
return None
versions_behind = get_versions_behind(serverjar_version, latest_version)
rich_table = Table(box=None)
rich_table.add_column("Name", style="bright_magenta")
rich_table.add_column("Installed V.", justify="right", style="green")
rich_table.add_column("Latest V.", justify="right", style="bright_green")
rich_table.add_column("Versions behind", justify="right", style="cyan")
rich_table.add_row(
file_server_jar_full_name,
serverjar_version,
str(latest_version),
str(versions_behind)
)
rich_console = Console()
rich_console.print(rich_table)
return None
def serverjar_papermc_update(
server_jar_version: str="latest",
mc_version: str=None,
file_server_jar_full_name: str=None,
serverjar_to_download: str=None
) -> bool:
"""
Handles the downloading of the papermc serverjar
:param server_jar_version: Version of the serverjar which should get downloaded
:param mc_version: Minecraft version
:param no_confirmation: If no confirmation message should pop up
:param file_server_jar_full_name: The old serverjar file
:param serverjar_to_download: The serverjar to download because it supports: paper, velocity, waterfall
This is used in the handle_input function
:returns: True/False if the serverjar was downloaded successfully
"""
config_values = config_value()
match config_values.connection:
case "local":
path_server_root = config_values.path_to_plugin_folder
# need help_path or else TypeError will be thrown
help_path = Path('/plugins')
help_path_str = str(help_path)
path_server_root = Path(str(path_server_root).replace(help_path_str, ''))
case _:
path_server_root = create_temp_plugin_folder()
# exit if the mc version can't be found
if file_server_jar_full_name == None and mc_version == None:
rich_print_error("Error: Please specifiy the minecraft version as third argument!")
return False
# if both the file name and the serverjar_to_download are emtpy then exit
if file_server_jar_full_name == None and serverjar_to_download == None:
rich_print_error("Error: Couldn't get serverjar name to download")
return False
if mc_version == None:
mc_version = get_version_group(file_server_jar_full_name)
if file_server_jar_full_name == None:
papermc_serverjar = serverjar_to_download
else:
papermc_serverjar = file_server_jar_full_name
if server_jar_version == "latest" or server_jar_version == None:
server_jar_version = find_latest_available_version(papermc_serverjar, mc_version)
# use rich console for nice colors
rich_console = Console()
rich_console.print(
f"\n [not bold][bright_white]● [bright_magenta]{papermc_serverjar.capitalize()}" + \
f" [cyan]→ [bright_green]{server_jar_version}"
)
if file_server_jar_full_name != None:
serverjar_version = get_installed_serverjar_version(file_server_jar_full_name)
if get_versions_behind(serverjar_version, server_jar_version) == 0:
rich_console.print(" [not bold][bright_green]No updates currently available!")
return False
try:
download_file_name = get_papermc_download_file_name(mc_version, server_jar_version, papermc_serverjar)
except KeyError:
rich_print_error(f" Error: This version wasn't found for {mc_version}")
rich_print_error(f" Reverting to latest version for {mc_version}")
try:
server_jar_version = find_latest_available_version(papermc_serverjar, mc_version)
download_file_name = get_papermc_download_file_name(mc_version, server_jar_version, papermc_serverjar)
except KeyError:
rich_print_error(
f" Error: Version {mc_version} wasn't found for {papermc_serverjar.capitalize()} in the papermc api"
)
return False
if "paper" in papermc_serverjar:
url = f"https://papermc.io/api/v2/projects/paper/versions/{mc_version}" + \
f"/builds/{server_jar_version}/downloads/{download_file_name}"
elif "waterfall" in papermc_serverjar:
url = f"https://papermc.io/api/v2/projects/waterfall/versions/{mc_version}" + \
f"/builds/{server_jar_version}/downloads/{download_file_name}"
elif "velocity" in papermc_serverjar:
url = f"https://papermc.io/api/v2/projects/velocity/versions/{mc_version}" + \
f"/builds/{server_jar_version}/downloads/{download_file_name}"
download_path = Path(f"{path_server_root}/{download_file_name}")
with Progress(transient=True) as progress:
header = {'user-agent': 'pluGET/1.0'}
r = requests.get(url, headers=header, stream=True)
try:
file_size = int(r.headers.get('Content-Length'))
# create progress bar
download_task = progress.add_task(" [cyan]Downloading...", total=file_size)
except TypeError:
# Content-lenght returned nothing
file_size = 0
with open(download_path, 'wb') as f:
# split downloaded data in chunks of 65536
for data in r.iter_content(chunk_size=65536):
f.write(data)
# don't show progress bar if no content-length was returned
if file_size == 0:
continue
progress.update(download_task, advance=len(data))
#f.flush()
file_size_data = convert_file_size_down(convert_file_size_down(file_size))
rich_console.print(" [not bold][bright_green]Downloaded[bright_magenta] " + (str(file_size_data)).rjust(9) + \
f" MB [cyan]→ [white]{download_path}")
if config_values.connection == "sftp":
sftp_session = sftp_create_connection()
sftp_upload_server_jar(sftp_session, download_path)
remove_temp_plugin_folder()
elif config_values.connection == "ftp":
ftp_session = ftp_create_connection()
ftp_upload_server_jar(ftp_session, download_path)
remove_temp_plugin_folder()
return True

View File

@ -0,0 +1,208 @@
"""
Handles the update checking and downloading of these serverjars:
Purpur
"""
import re
import requests
from pathlib import Path
from rich.table import Table
from rich.console import Console
from rich.progress import Progress
from src.handlers.handle_config import config_value
from src.utils.console_output import rich_print_error
from src.handlers.handle_sftp import sftp_create_connection, sftp_upload_server_jar
from src.handlers.handle_ftp import ftp_create_connection, ftp_upload_server_jar
from src.utils.utilities import \
api_do_request, create_temp_plugin_folder, remove_temp_plugin_folder, convert_file_size_down
from src.serverjar.serverjar_paper_velocity_waterfall import \
get_installed_serverjar_version, get_version_group, get_versions_behind
def find_latest_available_version(version_group) -> int:
"""
Gets the latest available version of the installed serverjar version
:param version_group: Minecraft version group of the serverjar
:returns: Latest available version as int
"""
url = f"https://api.purpurmc.org/v2/purpur/{version_group}/"
versions = api_do_request(url)
if "status" in versions: # Checks if the API returns a status. This means that there was an error.
return None
latest_version = versions["builds"]["all"][-1]
return latest_version
def get_purpur_download_file_name(mc_version, serverjar_version) -> str:
"""
Gets the download name from the purpur api and merge it together in the right format
:param mc_version: Minecraft version
:param serverjar_version: Version of the serverjar
:param file_server_jar_full_name: Serverjar name
:returns: Download name of the file
"""
url = f"https://api.purpurmc.org/v2/purpur/{mc_version}/{serverjar_version}/"
build_details = api_do_request(url)
purpur_build_version = build_details["build"]
purpur_project_name = build_details["project"]
purpur_mc_version = build_details["version"]
download_name = f"{purpur_project_name}-{purpur_mc_version}-{purpur_build_version}.jar"
return download_name
def serverjar_purpur_check_update(file_server_jar_full_name) -> None:
"""
Checks the installed purpur serverjar if an update is available
:param file_server_jar_full_name: Full name of the purpu server jar file name
:returns: None
"""
serverjar_version = get_installed_serverjar_version(file_server_jar_full_name)
if serverjar_version == None:
rich_print_error("Error: An error occured while checking the installed serverjar version")
return None
version_group = get_version_group(file_server_jar_full_name)
if version_group == None:
rich_print_error(
"Error: An error occured while checking the installed version group of the installed serverjar"
)
return None
latest_version = find_latest_available_version(version_group)
if latest_version == None:
rich_print_error("Error: An error occured while checking for the latest available version of the serverjar")
return None
versions_behind = get_versions_behind(serverjar_version, latest_version)
rich_table = Table(box=None)
rich_table.add_column("Name", style="bright_magenta")
rich_table.add_column("Installed V.", justify="right", style="green")
rich_table.add_column("Latest V.", justify="right", style="bright_green")
rich_table.add_column("Versions behind", justify="right", style="cyan")
rich_table.add_row(
file_server_jar_full_name,
serverjar_version,
str(latest_version),
str(versions_behind)
)
rich_console = Console()
rich_console.print(rich_table)
return None
def serverjar_purpur_update(
server_jar_version: str="latest",
mc_version: str=None,
file_server_jar_full_name: str=None
) -> bool:
"""
Handles the downloading of the papermc serverjar
:param server_jar_version: Version of the serverjar which should get downloaded
:param mc_version: Minecraft version
:param no_confirmation: If no confirmation message should pop up
:param file_server_jar_full_name: The old serverjar file
:returns: True/False if the serverjar was downloaded successfully
"""
config_values = config_value()
match config_values.connection:
case "local":
path_server_root = config_values.path_to_plugin_folder
# need help_path or else TypeError will be thrown
help_path = Path('/plugins')
help_path_str = str(help_path)
path_server_root = Path(str(path_server_root).replace(help_path_str, ''))
case _:
path_server_root = create_temp_plugin_folder()
# exit if the mc version can't be found
if file_server_jar_full_name == None and mc_version == None:
rich_print_error("Error: Please specifiy the minecraft version as third argument!")
return False
if mc_version == None:
mc_version = get_version_group(file_server_jar_full_name)
if server_jar_version == "latest" or server_jar_version == None:
server_jar_version = find_latest_available_version(mc_version)
if file_server_jar_full_name == None:
serverjar_name = "purpur"
else:
serverjar_name = file_server_jar_full_name
# use rich console for nice colors
rich_console = Console()
rich_console.print(
f"\n [not bold][bright_white]● [bright_magenta]{serverjar_name.capitalize()}" + \
f" [cyan]→ [bright_green]{server_jar_version}"
)
if file_server_jar_full_name != None:
serverjar_version = get_installed_serverjar_version(file_server_jar_full_name)
if get_versions_behind(serverjar_version, server_jar_version) == 0:
rich_console.print(" [not bold][bright_green]No updates currently available!")
return False
try:
download_file_name = get_purpur_download_file_name(mc_version, server_jar_version)
except KeyError:
rich_print_error(f" Error: This version wasn't found for {mc_version}")
rich_print_error(f" Reverting to latest version for {mc_version}")
try:
server_jar_version = find_latest_available_version(mc_version)
download_file_name = get_purpur_download_file_name(mc_version, server_jar_version)
except KeyError:
rich_print_error(
f" Error: Version {mc_version} wasn't found for {serverjar_name.capitalize()} in the purpur api"
)
return False
url = f"https://api.purpurmc.org/v2/purpur/{mc_version}/{server_jar_version}/download/"
download_path = Path(f"{path_server_root}/{download_file_name}")
with Progress(transient=True) as progress:
header = {'user-agent': 'pluGET/1.0'}
r = requests.get(url, headers=header, stream=True)
try:
file_size = int(r.headers.get('Content-Length'))
# create progress bar
download_task = progress.add_task(" [cyan]Downloading...", total=file_size)
except TypeError:
# Content-lenght returned nothing
file_size = 0
with open(download_path, 'wb') as f:
# split downloaded data in chunks of 65536
for data in r.iter_content(chunk_size=65536):
f.write(data)
# don't show progress bar if no content-length was returned
if file_size == 0:
continue
progress.update(download_task, advance=len(data))
#f.flush()
file_size_data = convert_file_size_down(convert_file_size_down(file_size))
rich_console.print(" [not bold][bright_green]Downloaded[bright_magenta] " + (str(file_size_data)).rjust(9) + \
f" MB [cyan]→ [white]{download_path}")
if config_values.connection == "sftp":
sftp_session = sftp_create_connection()
sftp_upload_server_jar(sftp_session, download_path)
remove_temp_plugin_folder()
elif config_values.connection == "ftp":
ftp_session = ftp_create_connection()
ftp_upload_server_jar(ftp_session, download_path)
remove_temp_plugin_folder()
return True

View File

@ -0,0 +1,139 @@
"""
Checks the installed serverjar for updates
"""
import os
from pathlib import Path
from rich.console import Console
from src.handlers.handle_config import config_value
from src.utils.console_output import rich_print_error
from src.handlers.handle_sftp import sftp_create_connection, sftp_list_files_in_server_root
from src.handlers.handle_ftp import ftp_create_connection, ftp_list_files_in_server_root
from src.serverjar.serverjar_paper_velocity_waterfall import serverjar_papermc_check_update, serverjar_papermc_update
from src.serverjar.serverjar_purpur import serverjar_purpur_check_update, serverjar_purpur_update
def get_installed_server_jar_file(config_values) -> str:
"""
Gets the file name of the installed server jar
:param config_values: Configuration values from pluGET config
:returns: Full file name of installed server jar
"""
match config_values.connection:
case "sftp":
connection = sftp_create_connection()
file_list_server_root = sftp_list_files_in_server_root(connection)
case "ftp":
connection = ftp_create_connection()
file_list_server_root = ftp_list_files_in_server_root(connection)
case _:
file_list_server_root = os.path.dirname(config_values.path_to_plugin_folder)
file_list_server_root = os.listdir(file_list_server_root)
file_server_jar_full_name = None
try:
for file in file_list_server_root:
try:
if ".jar" in file:
file_server_jar_full_name = file
break
except TypeError:
continue
except TypeError:
rich_print_error("Error: Serverjar couldn't be found")
return None
return file_server_jar_full_name
def check_update_available_installed_server_jar() -> None:
"""
Handles the checking of available updates of the installed server jar
:returns: None
"""
config_values = config_value()
file_server_jar_full_name = get_installed_server_jar_file(config_values)
if file_server_jar_full_name == None:
# print error and exit function
rich_print_error("Error: Serverjar couldn't be found")
return None
# TODO: Add other serverjars here
# Paper / Velocity / Waterfall
if "paper" in file_server_jar_full_name or \
"waterfall" in file_server_jar_full_name or \
"velocity" in file_server_jar_full_name:
serverjar_papermc_check_update(file_server_jar_full_name)
# Purpur
elif "purpur" in file_server_jar_full_name:
serverjar_purpur_check_update(file_server_jar_full_name)
else:
rich_print_error(f"[not bold]Error: [bright_magenta]{file_server_jar_full_name} [bright_red]isn't supported")
return None
def update_installed_server_jar(server_jar_version: str="latest") -> None:
"""
Handles the updating of the installed server jar
:returns: None
"""
config_values = config_value()
file_server_jar_full_name = get_installed_server_jar_file(config_values)
if file_server_jar_full_name == None:
# print error and exit function
rich_print_error("Error: Serverjar couldn't be found")
return None
# finding path which is used for deleting old server jar
match config_values.connection:
case "local":
path_server_root = config_values.path_to_plugin_folder
# need help_path or else TypeError will be thrown
help_path = Path('/plugins')
help_path_str = str(help_path)
path_server_root = Path(str(path_server_root).replace(help_path_str, ''))
case _:
path_server_root = config_values.remote_plugin_folder_on_server
path_server_root = str(path_server_root).replace(r'/plugins', '')
server_jar_path = f"{path_server_root}/{file_server_jar_full_name}"
rich_console = Console()
download_successfull = False
# TODO: Add other serverjars here
# Paper / Velocity / Waterfall
if "paper" in file_server_jar_full_name or \
"waterfall" in file_server_jar_full_name or \
"velocity" in file_server_jar_full_name:
download_successfull = serverjar_papermc_update(server_jar_version, None, file_server_jar_full_name, None)
# Purpur
elif "purpur" in file_server_jar_full_name:
download_successfull = serverjar_purpur_update(server_jar_version, None, file_server_jar_full_name)
else:
rich_print_error(f"[not bold]Error: [bright_magenta]{file_server_jar_full_name} [bright_red]isn't supported")
# remove old serverjar when the serverjar was sucessfully updated
if download_successfull is True:
match config_values.connection:
case "local":
os.remove(Path(server_jar_path))
case "sftp":
connection = sftp_create_connection()
connection.remove(server_jar_path)
case "ftp":
connection = ftp_create_connection()
connection.delete(server_jar_path)
rich_console.print(
" [not bold][bright_green]Deleted old server file [cyan]→ [white]" +
f"{file_server_jar_full_name}"
)
return None

8
src/settings.py Normal file
View File

@ -0,0 +1,8 @@
"""
Constant values
PLUGETVERSION = current version of pluGET
"""
# constant values
PLUGETVERSION = "1.7.2"

0
src/tests/__init__.py Normal file
View File

View File

@ -0,0 +1,31 @@
import unittest
from src.plugin import plugin_updatechecker
class TestCases(unittest.TestCase):
def test_get_plugin_file_name(self):
plugin_file_name = "LuckPerms-5.4.30.jar"
plugin_file_name_cropped = "LuckPerms"
result = plugin_updatechecker.get_plugin_file_name(plugin_file_name)
self.assertEqual(result, plugin_file_name_cropped)
def test_get_plugin_file_version(self):
plugin_file_name = "LuckPerms-5.4.30.jar"
plugin_version_cropped = "5.4.30"
result = plugin_updatechecker.get_plugin_file_version(plugin_file_name)
self.assertEqual(result, plugin_version_cropped)
def test_get_plugin_version_without_letters(self):
plugin_version = "VERSIONv5.4.30"
plugin_version_cropped = "5.4.30"
result = plugin_updatechecker.get_plugin_version_without_letters(plugin_version)
self.assertEqual(result, plugin_version_cropped)
def test_compare_plugin_version(self):
result = plugin_updatechecker.compare_plugin_version("5.4.30", "5.4.0")
result2 = plugin_updatechecker.compare_plugin_version("5.4.30", "8.7.60")
result3 = plugin_updatechecker.compare_plugin_version("5.4.30", "5.4.30")
self.assertEqual(result, True)
self.assertEqual(result2, False)
self.assertEqual(result3, False)

View File

@ -0,0 +1,61 @@
import unittest
from src.plugin import plugin_downloader
from src.utils import utilities
class TestCases(unittest.TestCase):
def test_handle_regex_plugin_name(self):
# Cropped name -> 'SUPERBPlugin'
plugin_name = "[1.13-5.49 ❤] >|> SUPERB Plugin <<💥| Now 150% OFF IN WINTER SALE IN SUMMER???"
plugin_name_cropped = "SUPERBPlugin"
result = plugin_downloader.handle_regex_plugin_name(plugin_name)
self.assertEqual(result, plugin_name_cropped)
def test_get_version_id_spiget(self):
# 21840 -> "Luckperms" in Version 5.4.30
result = plugin_downloader.get_version_id_spiget("28140", "5.4.30")
self.assertEqual(result, 455966)
def test_get_version_name_spiget(self):
# 455966 -> "5.4.30" from Luckperms
result = plugin_downloader.get_version_name_spiget("28140", 455966)
self.assertEqual(result, "5.4.30")
def test_get_download_path(self):
# local plugin folder
class config_values_local:
connection = "local"
local_seperate_download_path = True
local_path_to_seperate_download_path = "/local/path/plugins"
result=plugin_downloader.get_download_path(config_values_local)
self.assertEqual(result, config_values_local.local_path_to_seperate_download_path)
# plugin folder over sftp
class config_values_sftp:
connection = "sftp"
remote_seperate_download_path = True
remote_path_to_seperate_download_path = "/sftp/path/plugins"
result=plugin_downloader.get_download_path(config_values_sftp)
self.assertEqual(result, config_values_sftp.remote_path_to_seperate_download_path)
# plugin folder over ftp
class config_values_ftp:
connection = "ftp"
remote_seperate_download_path = True
remote_path_to_seperate_download_path = "/ftp/path/plugins"
result=plugin_downloader.get_download_path(config_values_ftp)
self.assertEqual(result, config_values_ftp.remote_path_to_seperate_download_path)
def test_convert_file_size_down(self):
# 100000 / 1024 = 97.66
result= utilities.convert_file_size_down(100000)
self.assertEqual(result, 97.66)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,42 @@
import unittest
from src.serverjar import serverjar_paper_velocity_waterfall
class TestCases(unittest.TestCase):
def test_get_installed_serverjar_version(self):
# paper-1.19-40.jar -> 40
serverjar_file_name = "paper-1.19-40.jar"
serverjar_version = "40"
result = serverjar_paper_velocity_waterfall.get_installed_serverjar_version(serverjar_file_name)
self.assertEqual(result, serverjar_version)
def test_get_version_group(self):
# 1.18.2 -> 1.18
mc_version = "1.18.2"
mc_version_group = "1.18.2"
result = serverjar_paper_velocity_waterfall.get_version_group(mc_version)
self.assertEqual(result, mc_version_group)
def test_find_latest_available_version(self):
# Get latest available paper version for 1.15.2 which should be 393
file_server_jar_full_name = "paper-1.15.2-40.jar"
version_group = "1.15.2"
result = serverjar_paper_velocity_waterfall.find_latest_available_version(
file_server_jar_full_name,
version_group
)
self.assertEqual(result, 393)
def test_get_versions_behind(self):
# 161 - 157 = 4
serverjar_version = 157
latest_version = 161
result = serverjar_paper_velocity_waterfall.get_versions_behind(serverjar_version, latest_version)
self.assertEqual(result, 4)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,40 @@
import unittest
from src.serverjar import serverjar_purpur
from src.serverjar import serverjar_paper_velocity_waterfall
class TestCases(unittest.TestCase):
def test_get_installed_serverjar_version(self):
# purpur-1.19-40.jar -> 40
serverjar_file_name = "purpur-1.19-40.jar"
serverjar_version = "40"
result = serverjar_paper_velocity_waterfall.get_installed_serverjar_version(serverjar_file_name)
self.assertEqual(result, serverjar_version)
def test_get_version_group(self):
# 1.18.2 -> 1.18
mc_version = "1.18.2"
mc_version_group = "1.18.2"
result = serverjar_paper_velocity_waterfall.get_version_group(mc_version)
self.assertEqual(result, mc_version_group)
def test_find_latest_available_version(self):
# Get latest available purpur version for 1.15.2 which should be '606'
file_server_jar_full_name = "purpur-1.15.2-40.jar"
version_group = "1.15.2"
result = serverjar_purpur.find_latest_available_version(version_group)
self.assertEqual(result, str(606))
def test_get_versions_behind(self):
# 161 - 157 = 4
serverjar_version = 157
latest_version = 161
result = serverjar_paper_velocity_waterfall.get_versions_behind(serverjar_version, latest_version)
self.assertEqual(result, 4)
if __name__ == "__main__":
unittest.main()

152
src/utils/console_output.py Normal file
View File

@ -0,0 +1,152 @@
"""
Handles the console on first startup of pluGET and prints logo and sets title
"""
import os
from rich.console import Console
from src.settings import PLUGETVERSION
def rich_print_error(error_message) -> None:
"""
Prints a formatted error message from rich
"""
console = Console()
console.print(error_message, style="bright_red")
return None
def rename_console_title() -> None:
"""
Renames the console title on first startup
"""
if os.name == "nt":
os.system("title " + "pluGET │ By Neocky")
return None
def clear_console() -> None:
"""
Clears the console on first startup
"""
os.system('cls' if os.name=='nt' else 'clear')
return None
def print_logo() -> None:
"""
Prints the logo of pluGET and the link to the github repo
"""
# use rich console
console = Console()
# line 1
console.print()
# line 2
console.print(" ██████",style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██████", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("███████", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("████████", style="bright_magenta", end='')
console.print("", style="bright_yellow")
# line 3
console.print(" ██", style="bright_magenta", end='')
console.print("╔══", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("╔════╝ ", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("╔════╝╚══", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("╔══╝", style="bright_yellow")
# line 4
console.print(" ██████", style="bright_magenta", end='')
console.print("╔╝", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("███", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("█████", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow")
# line 5
console.print(" ██", style="bright_magenta", end='')
console.print("╔═══╝ ", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("╔══╝ ", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow")
# line 6
console.print(" ██", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("███████", style="bright_magenta", end='')
console.print("╗╚", style="bright_yellow", end='')
console.print("██████", style="bright_magenta", end='')
console.print("╔╝╚", style="bright_yellow", end='')
console.print("██████", style="bright_magenta", end='')
console.print("╔╝", style="bright_yellow", end='')
console.print("███████", style="bright_magenta", end='')
console.print("", style="bright_yellow", end='')
console.print("██", style="bright_magenta", end='')
console.print("", style="bright_yellow")
# line 7
console.print(" ╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ", style="bright_yellow")
# line 8
console.print()
# line 9
console.print(" ┌────────────────────────────────────┐", style="bright_black")
# line 10
console.print(" │ [", style="bright_black", end='')
console.print("By Neocky", style="bright_magenta", end='')
console.print("] │ ", style="bright_black")
# line 11
console.print("", style="bright_black", end='')
console.print("https://github.com/Neocky/pluGET", style="link https://github.com/Neocky/pluGET", end='')
console.print("", style="bright_black")
# line 12
console.print(" └────────────────────────────────────┘", style="bright_black")
console.print(" ───────────────────────────────────────────────────")
return None
def print_console_logo() -> None:
"""
Prints the logo of pluGET if it is called from console
"""
console = Console()
console.print(f"[not bold][bright_magenta]pluGET [bright_green]{PLUGETVERSION} ", end='')
console.print("created by Neocky → ", end='')
console.print("https://github.com/Neocky/pluGET", style="link https://github.com/Neocky/pluGET")
return None

View File

@ -1,89 +0,0 @@
from os import system
from os import name
def consoleTitle():
system("title " + "pluGET │ By Neocky")
def clearConsole():
system('cls' if name=='nt' else 'clear')
# https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
class oColors:
standardWhite = "\033[0m"
brightBlack = "\033[90m"
brightRed = "\033[91m"
brightGreen = "\033[92m"
brightYellow = "\033[93m"
brightMagenta = "\033[95m"
def printLogo():
print()
print(oColors.brightYellow + r" ___ ___ ___ ___ ___ ___ ")
print(oColors.brightMagenta + r" /\ " + oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/"'\\' +
oColors.brightYellow + r"__\ " + oColors.brightMagenta + r"/"'\\' + oColors.brightYellow + r"__\ " +
oColors.brightMagenta + r"/\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/\ "+ oColors.brightYellow + r""'\\ ')
print(oColors.brightMagenta + r" /::\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/:/ " +
oColors.brightYellow + r"/ " + oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ " +
oColors.brightMagenta + r"/::\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/::\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"\:\ "+ oColors.brightYellow + r""'\\ ')
print(oColors.brightMagenta + r" /:/\:\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/:/ "+
oColors.brightYellow + r"/ " + oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ " +
oColors.brightMagenta + r"/:/\:\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/:/\:\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"\:\ "+ oColors.brightYellow + r""'\\ ')
print(oColors.brightMagenta + r" /::"'\\' + oColors.brightYellow + r"~" + oColors.brightMagenta + r"\:\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ " +
oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ ___ " + oColors.brightMagenta + r"/:/ \:\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/::"'\\'+ oColors.brightYellow + r"~" +
oColors.brightMagenta + r"\:\ "+ oColors.brightYellow + r"\ " + oColors.brightMagenta + r"/::\ "+
oColors.brightYellow + r""'\\')
print(oColors.brightMagenta + r" /:/\:\ \:"'\\'+ oColors.brightYellow + r"__\ " + oColors.brightMagenta + r"/:/"+
oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"/:/"+ oColors.brightYellow + r"__/ " +
oColors.brightMagenta + r"/"'\\'+ oColors.brightYellow + r"__\ " + oColors.brightMagenta + r"/:/"+
oColors.brightYellow + r"__/_" + oColors.brightMagenta + r"\:"'\\'+ oColors.brightYellow + r"__\ " +
oColors.brightMagenta + r"/:/\:\ \:"'\\' + oColors.brightYellow + r"__\ " +
oColors.brightMagenta + r"/:/\:"'\\'+ oColors.brightYellow + r"__\ ")
print(oColors.brightMagenta + r" " + oColors.brightMagenta + r"\/"+ oColors.brightYellow + r"__" +
oColors.brightMagenta + r"\:\/:/"+ oColors.brightYellow + r" / " + oColors.brightMagenta + r"\:"'\\'+
oColors.brightYellow + r" \ " + oColors.brightMagenta + r"\:"'\\' + oColors.brightYellow + r" \ " +
oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ " + oColors.brightMagenta + r"\:\ /\ \/" +
oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"\:"'\\' + oColors.brightYellow + r"~" +
oColors.brightMagenta + r"\:\ \/" + oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"/:/ \/"+
oColors.brightYellow + r"__/")
print(oColors.brightMagenta + r" \::/ "+ oColors.brightYellow + r"/ " + oColors.brightMagenta + r"\:\ "+
oColors.brightYellow + r"\ " + oColors.brightMagenta + r"\:\ /:/ "+ oColors.brightYellow + r"/ " +
oColors.brightMagenta + r"\:\ \:"'\\'+ oColors.brightYellow + r"__\ " + oColors.brightMagenta + r"\:\ \:"'\\'+
oColors.brightYellow + r"__\ " + oColors.brightMagenta + r"/:/ "+ oColors.brightYellow + r"/ ")
print(oColors.brightMagenta + r" \/"+ oColors.brightYellow + r"__/ " +
oColors.brightMagenta + r"\:\ " + oColors.brightYellow + r"\ " + oColors.brightMagenta + r"\:\/:/ "+
oColors.brightYellow + r"/ " + oColors.brightMagenta + r"\:\/:/ "+ oColors.brightYellow + r"/ " +
oColors.brightMagenta + r"\:\ \/"+ oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"\/"+
oColors.brightYellow + r"__/ ")
print(oColors.brightMagenta + r" \:"'\\' + oColors.brightYellow + r"__\ " +
oColors.brightMagenta + r"\::/ " + oColors.brightYellow + r"/ " + oColors.brightMagenta + r"\::/ " +
oColors.brightYellow + r"/ " + oColors.brightMagenta + r"\:"'\\' + oColors.brightYellow + r"__\ ")
print(oColors.brightMagenta + r" \/" + oColors.brightYellow + r"__/ " +
oColors.brightMagenta + r"\/" + oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"\/" +
oColors.brightYellow + r"__/ " + oColors.brightMagenta + r"\/" + oColors.brightYellow + r"__/ " +
oColors.standardWhite)
print()
print()
print(oColors.brightBlack + " ┌────────────────────────────────────┐" + oColors.standardWhite)
print(oColors.brightBlack + " │ [" + oColors.brightMagenta + "By Neocky" +oColors.brightBlack +
"] │ " + oColors.standardWhite)
print(oColors.brightBlack + "" + oColors.brightMagenta + "https://github.com/Neocky/pluGET" + oColors.brightBlack +
"" + oColors.standardWhite)
print(oColors.brightBlack + " └────────────────────────────────────┘" + oColors.standardWhite)
def printHorizontalLine():
print(" ─────────────────────────────────────────────────────────────────────────────────")
def printMainMenu():
printLogo()
printHorizontalLine()

View File

@ -1,175 +1,210 @@
# misc functions
"""
Holds all the utilitie code for pluGET and the webrequests function
"""
import os
import sys
import shutil
import requests
import shutil
import re
from pathlib import Path
from rich.console import Console
from rich.table import Table
from src.handlers.handle_sftp import sftp_create_connection
from src.handlers.handle_ftp import ftp_create_connection
from utils.consoleoutput import oColors
from handlers.handle_config import configurationValues
from handlers.handle_sftp import createSFTPConnection
from handlers.handle_ftp import createFTPConnection
from src.utils.console_output import rich_print_error
from src.handlers.handle_config import config_value
from src.settings import PLUGETVERSION
def getHelp():
print(oColors.brightYellow+ "Need help?" + oColors.standardWhite)
print("For a list of all commands: 'help command'")
print("Or check the docs here:")
print("https://github.com/Neocky/pluGET")
print("Or go to the official discord.")
print("The link for discord can also be found on Github!")
def get_command_help(command: str) -> None:
"""
Prints the help page for all commands and individual commands
:param command: Console command which the help page should show
:returns: None
"""
rich_console = Console()
rich_table = Table(box=None)
rich_table.add_column("Command", justify="left", style="bright_blue", no_wrap=True)
rich_table.add_column("Object", style="bright_magenta")
rich_table.add_column("Params", justify="left", style="cyan")
rich_table.add_column("Description", justify="left", style="white")
match command:
case "all":
rich_table.add_row("check", "Name/all", None, "Check for an update of an installed plugin")
rich_table.add_row("check", "serverjar", None, "Check for an update for the installed serverjar")
rich_table.add_row("exit", "./anything", None, "Exit pluGET")
rich_table.add_row("get", "Name/ID", None, "Downloads the latest version of a plugin")
rich_table.add_row("get-paper", "PaperVersion", "McVersion", "Downloads a specific PaperMc version")
rich_table.add_row("get-purpur", "PurpurVersion", "McVersion", "Downloads a specific Purpur version")
rich_table.add_row("get-velocity", "VelocityVersion", "McVersion", "Downloads a specific Velocity version")
rich_table.add_row(
"get-waterfall", "WaterfallVersion", "McVersion", "Downloads a specific waterfall version"
)
rich_table.add_row("help", "./anything", None, "Get specific help to the commands of pluGET")
rich_table.add_row("remove", "Name", None, "Delete an installed plugin from the plugin folder")
rich_table.add_row("search", "Name/all", None, "Search for a plugin and download the latest version")
rich_table.add_row("update", "Name/all", None, "Update installed plugins to the latest version")
rich_table.add_row("update", "serverjar", None, "Update the installed serverjar to the latest version")
case "check":
rich_table.add_row("check", "Name/all", None, "Check for an update of an installed plugin")
rich_table.add_row("check", "serverjar", None, "Check for an update for the installed serverjar")
case "exit":
rich_table.add_row("exit", "./anything", None, "Exit pluGET")
case "get":
rich_table.add_row("get", "Name/ID", None, "Downloads the latest version of a plugin")
case "get-paper":
rich_table.add_row("get-paper", "PaperVersion", "McVersion", "Downloads a specific PaperMc version")
case "get-purpur":
rich_table.add_row("get-purpur", "PurpurVersion", "McVersion", "Downloads a specific Purpur version")
case "get-velocity":
rich_table.add_row("get-velocity", "VelocityVersion", "McVersion", "Downloads a specific Velocity version")
case "get-waterfall":
rich_table.add_row(
"get-waterfall", "WaterfallVersion", "McVersion", "Downloads a specific Waterfall version"
)
case "help" | "all":
rich_table.add_row("help", "./anything", None, "Get specific help to the commands of pluGET")
case "remove":
rich_table.add_row("remove", "Name", None, "Delete an installed plugin from the plugin folder")
case "search":
rich_table.add_row("search", "Name/all", None, "Search for a plugin and download the latest version")
case "update":
rich_table.add_row("update", "Name/all", None, "Update installed plugins to the latest version")
rich_table.add_row("update", "serverjar", None, "Update the installed serverjar to the latest version")
case _:
rich_print_error(f"[not bold]Error: Help for command [bright_magenta]'{command}' [bright_red]not found!")
rich_print_error("Use [bright_blue]'help all' [bright_red]to get a list of all commands.")
return None
rich_console.print(rich_table)
def getCommandHelp(optionalParams):
if optionalParams == None:
optionalParams = 'all'
print(oColors.brightBlack + f"Help for command: {optionalParams}" +oColors.standardWhite)
print("┌────────────────┬─────────────────┬─────────────────┬────────────────────────────────────────────────────────┐")
print("│ Command │ Selected Object │ Optional Params │ Function │")
print("└────────────────┴─────────────────┴─────────────────┴────────────────────────────────────────────────────────┘")
while True:
if optionalParams == 'all':
print(oColors.brightBlack + " GENERAL:" + oColors.standardWhite)
print(" exit ./anything Exit pluGET")
print(" help ./anything Get general help")
print(" help command all/command Get specific help to the commands of pluGET")
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(" get Name/ID Version Downloads the latest version of a plugin")
print(" check Name/ID/all changelog Check for an update of an installed plugin")
print(" update Name/ID/all Update installed plugins to the latest version")
print(" search Name Search for a plugin and download the latest version")
print(" remove Name/ID Delete an installed plugin")
print(oColors.brightBlack + " SERVER SOFTWARE MANAGEMENT:" + oColors.standardWhite)
print(" check serverjar Check installed server software for an update")
print(" update serverjar Version/Latest Update installed server software to a specific version")
print(" get-paper PaperVersion McVersion Downloads a specific PaperMc version")
break
if optionalParams == 'exit':
print(oColors.brightBlack + " GENERAL:" + oColors.standardWhite)
print(" exit ./anything Exit pluGET")
break
if optionalParams == 'help':
print(oColors.brightBlack + " GENERAL:" + oColors.standardWhite)
print(" help ./anything Get general help")
print(" help command all/command Get specific help to the commands of pluGET")
break
if optionalParams == 'get':
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(print(" get Name/ID Version Downloads the latest version of a plugin"))
break
if optionalParams == 'check':
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(" check Name/ID/all Check for an update of an installed plugin")
print(oColors.brightBlack + " SERVER SOFTWARE MANAGEMENT:" + oColors.standardWhite)
print(" check serverjar Check installed server software for an update")
break
if optionalParams == 'update':
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(" update Name/ID/all Update installed plugins to the latest version")
print(oColors.brightBlack + " SERVER SOFTWARE MANAGEMENT:" + oColors.standardWhite)
print(" update serverjar Version/Latest Update installed server software to a specific version")
break
if optionalParams == 'search':
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(" search Name Search for a plugin and download the latest version")
break
if optionalParams == 'remove':
print(oColors.brightBlack + " PLUGIN MANAGEMENT:" + oColors.standardWhite)
print(" remove Name/ID Delete an installed plugin")
break
if optionalParams == 'get-paper':
print(oColors.brightBlack + " SERVER SOFTWARE MANAGEMENT:" + oColors.standardWhite)
print(" get-paper PaperVersion McVersion Downloads a specific PaperMc version")
break
else:
print(oColors.brightRed + "Error: Help for Command not found. Please try again. :(" + oColors.standardWhite)
break
def check_local_plugin_folder():
configValues = configurationValues()
if configValues.localPluginFolder:
if configValues.seperateDownloadPath:
pluginFolderPath = configValues.pathToSeperateDownloadPath
else:
pluginFolderPath = configValues.pathToPluginFolder
if not os.path.isdir(pluginFolderPath):
print(oColors.brightRed + "Plugin folder coulnd*t be found. Creating one..." + oColors.standardWhite)
try:
os.mkdir(pluginFolderPath)
except OSError:
print(oColors.brightRed + "Creation of directory %s failed" % pluginFolderPath)
print(oColors.brightRed + "Please check the config file!" + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
else:
print("Created directory %s" % pluginFolderPath)
def apiTest():
apiStatusUrl = 'https://api.spiget.org/v2/status'
def check_for_pluGET_update() -> None:
"""
Check with the github api if there is a new version for pluGET available and print download message if this is
the case
"""
response = api_do_request("https://api.github.com/repos/Neocky/pluGET/releases/latest")
# get '.1.6.10' as output
full_version_string = re.search(r"[\.?\d]*$", response["name"])
# remove '.' to get '1.6.10' as output
version = re.sub(r"^\.*", "", full_version_string.group())
console = Console()
try:
r = requests.get(apiStatusUrl)
pluget_installed_version_tuple = tuple(map(int, (PLUGETVERSION.split("."))))
plugin_latest_version_tuple = tuple(map(int, (version.split("."))))
except ValueError:
console.print("Couldn't check if new version of pluGET is available")
return None
if pluget_installed_version_tuple < plugin_latest_version_tuple:
print(f"A new version of pluGET is available: {version}")
console.print("Download it here: ", end='')
console.print("https://github.com/Neocky/pluGET", style="link https://github.com/Neocky/pluGET")
return None
def api_do_request(url) -> list:
"""
Handles the webrequest and returns a json list
"""
webrequest_header = {'user-agent': 'pluGET/1.0'}
try:
response = requests.get(url, headers=webrequest_header)
except:
rich_print_error("Error: Couldn't create webrequest")
# return None to make functions quit
return None
try:
api_json_data = response.json()
except:
rich_print_error("Error: Couldn't parse json of webrequest")
return None
return api_json_data
def api_test_spiget() -> None:
"""
Test if the Spiget api sends a 200 status code back
"""
try:
r = requests.get('https://api.spiget.org/v2/status')
except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError):
print(oColors.brightRed + "Couldn't make a connection to the API. Check you connection to the internet!" + oColors.standardWhite)
input("Press any key + enter to exit...")
rich_print_error("Error: Couldn't make a connection to the API. Check you connection to the internet!")
sys.exit()
if r.status_code != 200:
print(oColors.brightRed + "Problems with the API detected. Plese try it again later!" + oColors.standardWhite)
input("Press any key + enter to exit...")
rich_print_error("Error: Problems with the API detected. Plese try it again later!")
sys.exit()
return None
def check_requirements():
configValues = configurationValues()
apiTest()
check_local_plugin_folder()
if not configValues.localPluginFolder:
if configValues.sftp_useSftp:
createSFTPConnection()
else:
createFTPConnection()
def create_temp_plugin_folder() -> Path:
"""
Creates a temporary folder to store plugins inside
Returns full path of temporary folder
"""
path_temp_plugin_folder = Path("./TempSFTPFolder")
if os.path.isdir(path_temp_plugin_folder):
return path_temp_plugin_folder
def createTempPluginFolder():
configValues = configurationValues()
tempPluginFolder = Path("./TempSFTPFolder")
if not os.path.isdir(tempPluginFolder):
try:
os.mkdir(tempPluginFolder)
except OSError:
print(oColors.brightRed + "Creation of directory %s failed" % configValues.pathToPluginFolder)
print(oColors.brightRed + "Please check the config file!" + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit()
return tempPluginFolder
def deleteTempPluginFolder(tempPluginFolder):
try:
shutil.rmtree(tempPluginFolder)
os.mkdir(path_temp_plugin_folder)
except OSError:
rich_print_error(f"Error: Creation of directory {path_temp_plugin_folder} failed")
rich_print_error(" Please check for missing permissions in folder tree!")
sys.exit()
return path_temp_plugin_folder
def remove_temp_plugin_folder() -> None:
"""
Removes the temporary plugin folder and all content inside it
"""
try:
shutil.rmtree(Path("./TempSFTPFolder"))
except OSError as e:
print ("Error: %s - %s." % (e.filename, e.strerror))
rich_print_error(f"Error: {e.filename} - {e.strerror}")
return
def calculateFileSizeMb(downloadFileSize):
fileSizeDownload = int(downloadFileSize)
fileSizeMb = fileSizeDownload / 1024 / 1024
roundedFileSize = round(fileSizeMb, 2)
return roundedFileSize
def convert_file_size_down(file_size) -> float:
"""
Convert the size of the number one down. E.g. MB -> KB through division with 1024
"""
converted_file_size = (int(file_size)) / 1024
converted_file_size = round(converted_file_size, 2)
return converted_file_size
def calculateFileSizeKb(downloadFileSize):
fileSizeDownload = int(downloadFileSize)
fileSizeKb = fileSizeDownload / 1024
roundedFileSize = round(fileSizeKb, 2)
return roundedFileSize
def check_local_plugin_folder(config_values) -> None:
"""
Check if a local plugin folder exists and if not exit the programm
"""
if config_values.local_seperate_download_path:
plugin_folder_path = config_values.local_path_to_seperate_download_path
else:
plugin_folder_path = config_values.path_to_plugin_folder
if not os.path.isdir(plugin_folder_path):
rich_print_error(f"Error: Local plugin folder '{plugin_folder_path}' couldn't be found! \
\n Check the config and try again!")
sys.exit()
return None
def check_requirements() -> None:
"""
Check if the plugin folders are available
"""
config_values = config_value()
match config_values.connection:
case "local":
check_local_plugin_folder(config_values)
case "sftp":
sftp_create_connection()
case "ftp":
ftp_create_connection()
return None

View File

@ -1,9 +0,0 @@
# Handles the web requests
import requests
def doAPIRequest(url):
headers = {'user-agent': 'pluGET/1.0'}
response = requests.get(url, headers=headers)
packageDetails = response.json()
return packageDetails