Compare commits

..

No commits in common. "main" and "v1.6.6" have entirely different histories.
main ... v1.6.6

36 changed files with 1681 additions and 2708 deletions

2
.github/FUNDING.yml vendored
View File

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

3
.gitignore vendored
View File

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

284
README.md
View File

@ -1,11 +1,7 @@
<p align="center"> <p align="center">
<picture> <img src="https://i.ibb.co/JyCxnQn/logoreal.png" alt="pluGET" border="0"></a>
<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>
<p align="center"> <p align="center">
@ -21,12 +17,12 @@
# pluGET # 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://user-images.githubusercontent.com/13088544/177011216-1360d444-278a-475e-9863-966c48c60ba7.gif" alt="pluGET.gif" border="0" /> <img src="https://www.bildhost.com/images/2021/06/27/ezgif-1-28e102202188.gif" alt="pluGET.gif" border="0" />
<details> <details>
<summary>Old Screenshots</summary> <summary>Screenshots</summary>
`check all` to check installed plugins for updates: `check all` to check installed plugins for updates:
![screenshot1](https://i.ibb.co/QM7xh7w/pluget-checkall-small.png) ![screenshot1](https://i.ibb.co/QM7xh7w/pluget-checkall-small.png)
@ -39,24 +35,43 @@ A powerful package manager which updates [Plugins](https://www.spigotmc.org/reso
</details> </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> <img src="https://i.ibb.co/82dnyrK/image.png" alt="meme" border="0" height="350" width="350"></a>
# Features
- Works locally or through SFTP/FTP ## Issues? Found a bug?
- Runs directly from the console with command line arguments [Create an issue.](https://github.com/Neocky/pluGET/issues/new/choose)
- 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/) ## About
- [Purpur](https://purpurmc.org/) This is a package manager for minecraft [Spigot](https://www.spigotmc.org/) servers and its forks (e.g. [PaperMC](https://papermc.io/)).
- [Waterfall](https://papermc.io/downloads#Waterfall) This is a standalone program written in python.
- [Velocity](https://papermc.io/downloads#Velocity) 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/)
There are more features in the work. Check [Projects](https://github.com/Neocky/pluGET/projects) for a complete list. There are more features in the work. Check [Projects](https://github.com/Neocky/pluGET/projects) for a complete list.
@ -64,160 +79,183 @@ There are more features in the work. Check [Projects](https://github.com/Neocky/
[Get the latest release here.](https://github.com/Neocky/pluGET/releases) [Get the latest release here.](https://github.com/Neocky/pluGET/releases)
# Donations ## Donations
If you feel like showing your love and/or appreciation for this project then how about buying me a coffee? ☕🤎 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) <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>
## Need help?
# Need help? [<img src="https://i.ibb.co/CMKbT0L/rsz-1rsz-discord.png" alt="Discord" width="272"/>](https://discord.gg/475Uf4NBPF)
[<img src="https://i.ibb.co/PQv3KtJ/Discord-Logo-Wordmark-Color.png" alt="Discord" width="300"/>](https://discord.gg/475Uf4NBPF)
# Installation ## Installation
## 1. Python 3.10.4 ### 1. Python
Python needs to be installed on your machine. Get it [here](https://www.python.org/downloads/). 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.
## 2. Dependencies #### Manually
In order to install dependencies run the `install_requirements_WINDOWS/LINUX` file, of course depending on your system. Execute this command in the `\plugGET` folder:
```python
py -m pip install -r requirements.txt
```
## 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. ### 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.
Edit the config to your needs and relaunch pluGET. Edit the config to your needs and relaunch pluGET.
**Now you are good to go!** **Now you are good to go!**
## 4. Running the program ## Start pluGET
Execute the `pluget.py` file with python in the `\pluGET` folder. ### Windows:
Execute the `launcher.bat` in the `\pluGET` folder.
This will launch pluGET correctly. This will launch pluGET correctly.
```python > Sometimes the security warning `Windows protected your PC` comes when launching the `launcher.bat` file.
# Windows: > This is a normal behaviour from the windows defender because this is a unknown `.bat` file.
py pluget.py > To run the `launcher.bat` anyway, click `More Info` and then `Run anyway` when the message pops up.
# Linux
python3 pluget.py ### 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
``` ```
# Usage > On both OS you can also launch the `src/__main__.py` file.
## Usage
> As always, if you update plugins, shut down your server! > As always, if you update plugins, shut down your server!
## Commands: 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)
### • Show the information about all commands. ### General
<!-- <details> #### Command help:
`help command [all/command]`
```
help command
```
<details>
<summary>Output</summary> <summary>Output</summary>
![Example output](https://i.ibb.co/9VZCjD6/pluget-help2.png) ![Output](https://i.ibb.co/9VZCjD6/pluget-help2.png)
</details> --> </details>
### Manage Plugins
#### Download the latest update of a specific package:
`get [pluginID/pluginName]`
``` ```
help command [all/command] get 'pluginID'
```
or:
```
get 'pluginName'
```
#### Check all plugins/one specific plugin for updates with optional changelog output:
`check [all/pluginName] [changelog]`
```
check all
```
or:
```
check 'pluginName' changelog
``` ```
### • Exit program: <details>
```
exit .
```
### • Get link to this page:
```
help .
```
## Manage Plugins
### • Download the latest update of a specific package:
```
get [pluginID/pluginName]
```
### • Check all plugins/one specific plugin for updates with optional changelog output:
<!-- <details>
<summary>Output</summary> <summary>Output</summary>
![Output](https://i.ibb.co/VmSNh6K/pluget-checkall.png) ![Output](https://i.ibb.co/VmSNh6K/pluget-checkall.png)
</details> --> </details>
```
check [all/pluginName] [changelog]
```
### • Update all plugins/one specific plugin: #### Update all plugins/one specific plugin:
`update [all/pluginName]`
``` ```
update [all/pluginName] update all
``` ```
or:
### • Remove a plugin with the ID/Name:
``` ```
remove [pluginID/pluginName] update 'pluginName'
``` ```
#### Remove a plugin with the ID/Name:
### • Search for a plugin: `remove [pluginID/pluginName]`
``` ```
search [pluginName] remove 'pluginID'
``` ```
or:
## Manage Server Software ```
remove 'pluginName'
### • Check installed server software for updates: ```
#### Search for a plugin:
`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`
``` ```
check serverjar check serverjar
``` ```
### Update installed server software to latest/specific version:
### • Update installed server software to latest/specific version: `update serverjar [Version]`
``` ```
update serverjar [Version] update serverjar 'PaperMCVersion'
```
### Download specific paper version:
`get-paper [paperBuild] [minecraftVersion]`
```
get-paper 550 1.16.5
```
or:
```
get-paper 321
``` ```
### • Download specific paper version: ## Known problems
```
get-paper [paperBuild] [minecraftVersion]
```
### • Download specific waterfall version: ### Can't get latest version/Update available
```
get-waterfall [waterfallBuild] [minecraftVersion]
```
### • Download specific velocity version: #### Inconsistent Names and Versions
``` For example:
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](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`. 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. 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. > 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. Download the plugins with the `get [pluginName]` command to make them detectable for pluGET.
After downloading EssentialsX with `get EssentialsX` and using `check all`: After downloading EssentialsX with `get EssentialsX` and using `check all`:
![EssentialsX](https://i.ibb.co/ws5wHTj/essentialsx-2.png) ![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. EssentialsX is now detected from pluGET and can update automatically when a new version comes out.
### Bukkit plugins #### Bukkit plugins
Example: For example:
![worldguard](https://i.ibb.co/7NJ9HRG/pluget-checkallonlyone.png) ![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. 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/). 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/). pluGET supports currently only plugins from [Spigot](https://www.spigotmc.org/resources/).
In this example this is a bukkit plugin. In this example this is a bukkit plugin.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,126 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

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

View File

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

34
installer.bat Normal file
View File

@ -0,0 +1,34 @@
@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

4
launcher.bat Normal file
View File

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

2
launcher.sh Normal file
View File

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

View File

@ -1,45 +0,0 @@
"""
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

@ -12,4 +12,3 @@ rich >= 9.13.0
commonmark >= 0.9.1 commonmark >= 0.9.1
Pygments >= 2.8.1 Pygments >= 2.8.1
typing_extensions >= 3.7.4.3 typing_extensions >= 3.7.4.3
ruamel.yaml >= 0.17.21

16
src/__main__.py Normal file
View File

@ -0,0 +1,16 @@
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,109 +1,87 @@
""""
Handles the logic for the config validation, reading and creating
"""
import os import os
import sys import sys
import ruamel.yaml import configparser
from pathlib import Path from pathlib import Path
from rich.console import Console
from utils.consoleoutput import oColors
class config_value(): class configurationValues:
"""
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): def __init__(self):
yaml = ruamel.yaml.YAML() config = configparser.ConfigParser()
with open("pluGET_config.yaml", "r") as config_file: config.sections()
data = yaml.load(config_file) config.read("config.ini")
self.connection = str(data["Connection"]).lower() localPluginFolder = config['General']['UseLocalPluginFolder']
self.path_to_plugin_folder = Path(data["Local"]["PathToPluginFolder"]) self.pathToPluginFolder = Path(config['Local - This Machine']['PathToPluginFolder'])
self.local_seperate_download_path = True if data["Local"]["SeperateDownloadPath"] == True else False seperateDownloadPath = config['Local - This Machine']['SeperateDownloadPath']
self.local_path_to_seperate_download_path = Path(data["Local"]["PathToSeperateDownloadPath"]) self.pathToSeperateDownloadPath = Path(config['Local - This Machine']['PathToSeperateDownloadPath'])
self.server = data["Remote"]["Server"]
self.username = data["Remote"]["Username"] self.sftp_server = config['SFTP - Remote Server']['Server']
self.password = data["Remote"]["Password"] self.sftp_user = config['SFTP - Remote Server']['Username']
self.sftp_port = int(data["Remote"]["SFTP_Port"]) self.sftp_password = config['SFTP - Remote Server']['Password']
self.ftp_port = int(data["Remote"]["FTP_Port"]) sftp_port = config['SFTP - Remote Server']['SFTPPort']
self.remote_seperate_download_path = True if data["Remote"]["SeperateDownloadPath"] == True else False self.sftp_folderPath = config['SFTP - Remote Server']['PluginFolderOnServer']
self.remote_path_to_seperate_download_path = data["Remote"]["PathToSeperateDownloadPath"] sftp_useSftp = config['SFTP - Remote Server']['USE_SFTP']
self.remote_plugin_folder_on_server = data["Remote"]["PluginFolderOnServer"] 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
def check_config() -> None: def checkConfig():
""" configAvailable = os.path.isfile("config.ini")
Check if there is a pluGET_config.yml file in the same folder as pluget.py and if not create a new config if not configAvailable:
and exit the programm createConfig()
""" print(oColors.brightRed + "Config created. Edit config before executing again!" + oColors.standardWhite)
if not os.path.isfile("pluGET_config.yaml"):
create_config()
return None
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
#
# What should be used for the connection (local, sftp, ftp)
Connection: local
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...") input("Press any key + enter to exit...")
sys.exit() sys.exit()
def validate_config() -> None: def createConfig():
""" config = configparser.ConfigParser(allow_no_value=True)
Validates the config variables after config class is loaded and exit if error is detected and print error config['General'] = {}
""" config['General'][';'] = 'If a local plugin folder exists (True/False) (If False SFTP/FTP will be used):'
accepted_values = [ config['General']['UseLocalPluginFolder'] = 'True'
("local", "sftp", "ftp")
] config['Local - This Machine'] = {}
# exit afterwards if there is an error in config config['Local - This Machine']['PathToPluginFolder'] = 'C:/Users/USER/Desktop/plugins'
exit_afterwards = False config['Local - This Machine'][';'] = 'For a different folder to store the updated plugins change to (True/False) and the path below'
config = config_value() config['Local - This Machine']['SeperateDownloadPath'] = 'False'
# rich console for nice colors config['Local - This Machine']['PathToSeperateDownloadPath'] = 'C:/Users/USER/Desktop/plugins'
console = Console()
if config.connection not in accepted_values[0]: config['SFTP - Remote Server'] = {}
console.print(f"Error in Config! Accepted values for key 'Connection' are {accepted_values[0]}", config['SFTP - Remote Server']['Server'] = '0.0.0.0'
style="bright_red") config['SFTP - Remote Server']['Username'] = 'user'
exit_afterwards = True config['SFTP - Remote Server']['Password'] = 'password'
if exit_afterwards: config['SFTP - Remote Server'][';'] = 'If a different Port for SFTP needs to be used (Works only for SFTP)'
sys.exit() 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'
with open('config.ini', 'w') as configfile:
config.write(configfile)

View File

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

View File

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

View File

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

View File

@ -1,245 +1,148 @@
"""
File and functions which handle the download of the specific plugins
"""
import re import re
import urllib.request
from urllib.error import HTTPError
from pathlib import Path from pathlib import Path
import requests
from rich.table import Table from utils.consoleoutput import oColors
from rich.console import Console from utils.web_request import doAPIRequest
from rich.progress import Progress from utils.utilities import createTempPluginFolder, deleteTempPluginFolder, calculateFileSizeKb, calculateFileSizeMb
from handlers.handle_config import configurationValues
from src.utils.utilities import convert_file_size_down, remove_temp_plugin_folder, create_temp_plugin_folder from handlers.handle_sftp import sftp_upload_file, createSFTPConnection
from src.utils.utilities import api_do_request from handlers.handle_ftp import ftp_upload_file, createFTPConnection
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 handle_regex_plugin_name(full_plugin_name) -> str: def handleRegexPackageName(packageNameFull):
""" packageNameFull2 = packageNameFull
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 # trims the part of the package that has for example "[1.1 Off]" in it
unwanted_plugin_name = re.search(r'(^\[+[a-zA-Z0-9\s\W*\.*\-*\+*\%*\,]*\]+)', full_plugin_name) unwantedpackageName = re.search(r'(^\[+[a-zA-Z0-9\s\W*\.*\-*\+*\%*\,]*\]+)', packageNameFull)
if bool(unwanted_plugin_name): unwantedpackageNamematch = bool(unwantedpackageName)
unwanted_plugin_name_string = unwanted_plugin_name.group() if unwantedpackageNamematch:
full_plugin_name = full_plugin_name.replace(unwanted_plugin_name_string, '') unwantedpackageNameString = unwantedpackageName.group()
packageNameFull2 = packageNameFull.replace(unwantedpackageNameString, '')
# gets the real plugin_name "word1 & word2" is not supported only gets word1 # gets the real packagename "word1 & word2" is not supported only gets word 1
plugin_name = re.search(r'([a-zA-Z]\d*)+(\s?\-*\_*[a-zA-Z]\d*\+*\-*\'*)+', full_plugin_name) packageName = re.search(r'([a-zA-Z]\d*)+(\s?\-*\_*[a-zA-Z]\d*\+*\-*\'*)+', packageNameFull2)
try: packageNameFullString = packageName.group()
plugin_name_full_string = plugin_name.group() packageNameOnly = packageNameFullString.replace(' ', '')
found_plugin_name = plugin_name_full_string.replace(' ', '') return packageNameOnly
except AttributeError:
found_plugin_name = unwanted_plugin_name_string
return found_plugin_name
def get_version_id_spiget(plugin_id, plugin_version) -> str: def getVersionID(packageId, packageVersion):
""" if packageVersion == None or packageVersion == 'latest':
Returns the version id of the plugin url = f"https://api.spiget.org/v2/resources/{packageId}/versions/latest"
""" response = doAPIRequest(url)
if plugin_version == None or plugin_version == 'latest': versionId = response["id"]
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/latest" return versionId
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/{plugin_id}/versions?size=100&sort=-name" url = f"https://api.spiget.org/v2/resources/{packageId}/versions?size=100&sort=-name"
version_list = api_do_request(url) versionList = doAPIRequest(url)
if version_list == None:
return None for packages in versionList:
for plugins in version_list: packageUpdate = packages["name"]
plugin_update = plugins["name"] versionId = packages["id"]
version_id = plugins["id"] if packageUpdate == packageVersion:
if plugin_update == plugin_version: return versionId
return version_id return versionList[0]["id"]
return version_list[0]["id"]
def get_version_name_spiget(plugin_id, plugin_version_id) -> str: def getVersionName(packageId, versionId):
""" url = f"https://api.spiget.org/v2/resources/{packageId}/versions/{versionId}"
Returns the name of a specific version response = doAPIRequest(url)
""" versionName = response["name"]
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/{plugin_version_id}" return versionName
response = api_do_request(url)
if response == None:
return None
version_name = response["name"]
return version_name
def get_download_path(config_values) -> str: def searchPackage(resourceName):
""" configValues = configurationValues()
Reads the config and gets the path of the plugin folder url = f"https://api.spiget.org/v2/search/resources/{resourceName}?field=name&sort=-downloads"
""" packageName = doAPIRequest(url)
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
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:
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()
# 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:
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 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:
download_path = get_download_path(config_values)
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 i = 1
for found_plugin in plugin_search_results: print(oColors.brightBlack + f"Searching: {resourceName}" + oColors.standardWhite)
plugin_name = handle_regex_plugin_name(found_plugin["name"]) print("┌─────┬─────────────────────────────┬───────────┬──────────────────────────────────────────────────────────────────────┐")
plugin_downloads = found_plugin["downloads"] print("│ No. │ Name │ Downloads │ Description │")
plugin_description = found_plugin["tag"] print("└─────┴─────────────────────────────┴───────────┴──────────────────────────────────────────────────────────────────────┘")
rich_table.add_row(str(i), plugin_name, str(plugin_downloads), plugin_description) for resource in packageName:
i += 1 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
# print table from rich resourceSelected = int(input("Select your wanted resource (No.)(0 to exit): "))
rich_console = Console() if resourceSelected != 0:
rich_console.print(rich_table) 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
try:
getSpecificPackage(resourceId, pluginDownloadPath)
except HTTPError as err:
print(oColors.brightRed + f"Error: {err.code} - {err.reason}" + oColors.standardWhite)
try:
plugin_selected = input("Select your wanted resource (No.)(0 to exit): ") def downloadSpecificVersion(resourceId, downloadPath, versionID='latest'):
except KeyboardInterrupt: configValues = configurationValues()
return None if versionID != 'latest':
if plugin_selected == "0": #url = f"https://spigotmc.org/resources/{resourceId}/download?version={versionID}"
return None print(oColors.brightRed + "Sorry but specific version downloads aren't supported because of cloudflare protection. :(" + oColors.standardWhite)
try: print(oColors.brightRed + "Reverting to latest version." + oColors.standardWhite)
plugin_selected = int(plugin_selected) - 1
plugin_selected_id = plugin_search_results[plugin_selected]["id"] url = f"https://api.spiget.org/v2/resources/{resourceId}/download"
except ValueError: #url = f"https://api.spiget.org/v2/resources/{resourceId}/versions/latest/download" #throws 403 forbidden error...cloudflare :(
rich_print_error("Error: Input wasn't a number! Please try again!")
return None urrlib_opener = urllib.request.build_opener()
except IndexError: urrlib_opener.addheaders = [('User-agent', 'pluGET/1.0')]
rich_print_error("Error: Number was out of range! Please try again!") urllib.request.install_opener(urrlib_opener)
return None
selected_plugin_name = handle_regex_plugin_name(plugin_search_results[plugin_selected]["name"]) remotefile = urllib.request.urlopen(url)
rich_console.print(f"\n [not bold][bright_white]● [bright_magenta]{selected_plugin_name} [bright_green]latest") filesize = remotefile.info()['Content-Length']
get_specific_plugin_spiget(plugin_selected_id) 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}")
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)
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)
else:
downloadSpecificVersion(resourceId, downloadPackagePath, versionId)
if not configValues.localPluginFolder:
deleteTempPluginFolder(downloadPath)

View File

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

View File

@ -1,580 +1,482 @@
"""
Handles the plugin checking and updating
"""
import os import os
import re import re
import io import io
from pathlib import Path import base64
import zipfile
from rich.progress import track
from rich.table import Table
from rich.console import Console
from urllib.error import HTTPError
from zipfile import ZipFile from zipfile import ZipFile
from urllib.error import HTTPError
from pathlib import Path
from rich.progress import track
from src.handlers.handle_config import config_value from utils.consoleoutput import oColors
from src.handlers.handle_sftp import sftp_create_connection, sftp_download_file, sftp_validate_file_attributes, sftp_list_all from utils.web_request import doAPIRequest
from src.handlers.handle_ftp import ftp_create_connection, ftp_download_file, ftp_validate_file_attributes, ftp_list_all from handlers.handle_config import configurationValues
from src.plugin.plugin_downloader import get_specific_plugin_spiget, get_download_path from handlers.handle_sftp import createSFTPConnection, sftp_listAll, sftp_downloadFile, sftp_validateFileAttributes
from src.utils.console_output import rich_print_error from handlers.handle_ftp import createFTPConnection, ftp_listAll, ftp_downloadFile, ftp_validateFileAttributes
from src.utils.utilities import api_do_request, create_temp_plugin_folder, remove_temp_plugin_folder from plugin.plugin_downloader import getSpecificPackage
from utils.utilities import createTempPluginFolder, deleteTempPluginFolder
class Plugin(): def createPluginList():
"""
Create plugin class to store installed plugins inside it
"""
def __init__(
self,
plugin_file_name : str,
plugin_name : str,
plugin_file_version : str,
plugin_latest_version : str,
plugin_is_outdated : bool,
plugin_repository : str,
plugin_repository_data : list
) -> None:
self.plugin_file_name = plugin_file_name
self.plugin_name = plugin_name
self.plugin_file_version = plugin_file_version
self.plugin_latest_version = plugin_latest_version
self.plugin_is_outdated = plugin_is_outdated
self.plugin_repository = plugin_repository
self.plugin_repository_data = plugin_repository_data
@staticmethod
def create_plugin_list() -> list:
"""
Creates a global array list to store plugins
"""
global INSTALLEDPLUGINLIST global INSTALLEDPLUGINLIST
INSTALLEDPLUGINLIST = [] INSTALLEDPLUGINLIST = []
return INSTALLEDPLUGINLIST return INSTALLEDPLUGINLIST
@staticmethod def addToPluginList(localFileName, pluginId, versionId, plugin_latest_version, plugin_is_outdated):
def add_to_plugin_list( INSTALLEDPLUGINLIST.append([localFileName, pluginId, versionId, plugin_latest_version, plugin_is_outdated])
plugin_file_name: str,
plugin_name : str,
plugin_file_version : str,
plugin_latest_version : str,
plugin_is_outdated : bool,
plugin_repository : str,
plugin_repository_data : list
) -> None:
"""
Adds a plugin to global installed plugin lists
"""
INSTALLEDPLUGINLIST.append(Plugin(
plugin_file_name,
plugin_name,
plugin_file_version,
plugin_latest_version,
plugin_is_outdated,
plugin_repository,
plugin_repository_data
))
return None
def get_plugin_file_name(plugin_full_name: str) -> str: def getFileName(pluginName):
""" pluginNameFull = pluginName
Finds the full plugin name of the given string pluginVersion = re.search(r'([\d.]+[.jar]+)', pluginNameFull)
Example LuckPerms-5.4.30.jar -> Luckperms
:param plugin_full_name: Full filename of plugin
:returns: Full plugin name
"""
plugin_full_name2 = plugin_full_name
# find number.jar
plugin_file_version = re.search(r'([\d.]+[.jar]+)', plugin_full_name2)
try: try:
plugin_file_version_full = plugin_file_version.group() pluginVersionFull = pluginVersion.group()
except AttributeError: except AttributeError:
plugin_file_version_full = plugin_file_version pluginVersionFull = pluginVersion
# remove number from plugin name pluginNameOnlyy = pluginNameFull.replace(pluginVersionFull, '')
plugin_name_only = plugin_full_name2.replace(plugin_file_version_full, '') pluginNameOnly = re.sub(r'(\-$)', '', pluginNameOnlyy)
# remove - from plugin name pluginNameOnlyy = re.sub(r'(\-v$)', '', pluginNameOnly)
plugin_name_only = re.sub(r'(\-$)', '', plugin_name_only) return pluginNameOnlyy
# remove -v from plugin name
plugin_name_only = re.sub(r'(\-v$)', '', plugin_name_only)
return plugin_name_only
def get_plugin_file_version(plugin_full_name: str) -> str: def getFileVersion(pluginName):
""" pluginNameFull = pluginName
Gets the version of the plugin pluginVersion = re.search(r'([\d.]+[.jar]+)', pluginNameFull)
pluginVersionFull = pluginVersion.group()
:param plugin_full_name: Full filename of plugin pluginVersionString = pluginVersionFull.replace('.jar', '')
if pluginVersionString.endswith('.'):
:returns: Version of plugin as string pluginVersionString = ''
""" if pluginVersionString == '':
plugin_file_version = re.search(r'([\d.]+[.jar]+)', plugin_full_name) pluginVersionString = eggCrackingJar(pluginNameFull, 'version')
plugin_file_version = plugin_file_version.group() return pluginVersionString
plugin_file_version = plugin_file_version.replace('.jar', '')
if plugin_file_version.endswith('.'):
print("get_plugin_file_version endswith .")
plugin_file_name, plugin_file_version = egg_cracking_jar(plugin_full_name)
return plugin_file_version
def get_latest_plugin_version_spiget(plugin_id : str) -> str: def getLatestPluginVersion(pluginId):
""" url = f"https://api.spiget.org/v2/resources/{pluginId}/versions/latest"
Gets the latest spigot plugin version latestUpdateSearch = doAPIRequest(url)
versionLatestUpdate = latestUpdateSearch["name"]
:param plugin_id: Plugin Spigot ID return versionLatestUpdate
:returns: Name of the latest update
"""
url = f"https://api.spiget.org/v2/resources/{plugin_id}/versions/latest"
latest_update_search = api_do_request(url)
return str(latest_update_search["name"])
def create_plugin_version_tuple(plugin_version_string : str) -> tuple: def getUpdateDescription(pluginId):
""" url = f"https://api.spiget.org/v2/resources/{pluginId}/updates?size=1&sort=-date"
Create a tuple of all version numbers latestDescriptionSearch = doAPIRequest(url)
versionLatestDescription = latestDescriptionSearch[0]["description"]
:param plugin_version_string: Plugin Version versionLatestDescription = base64.b64decode(versionLatestDescription)
versionLatestDescriptionText =versionLatestDescription.decode('utf-8')
:returns: Tuple of all version numbers htmlRegex = re.compile('<.*?>')
""" versionLatestDescriptionText = re.sub(htmlRegex, '', versionLatestDescriptionText)
return tuple(map(int, (plugin_version_string.split(".")))) linesChangelogDescription = versionLatestDescriptionText.split("\n")
nonEmptyLines = [line for line in linesChangelogDescription if line.strip() != ""]
stringnonEmptyLines = ""
for line in nonEmptyLines:
stringnonEmptyLines += line + "\n"
stringnonEmptyLines = stringnonEmptyLines[:-1]
return stringnonEmptyLines
def get_plugin_version_without_letters(plugin_version_string : str) -> str: def versionTuple(versionString):
""" return tuple(map(int, (versionString.split("."))))
Returns the version without letters from the plugin version
:param plugin_version_string: Plugin Version
:returns: Plugin version without letters
"""
return re.sub(r'([A-Za-z]*)', '', plugin_version_string)
def compare_plugin_version(plugin_latest_version : str, plugin_file_version : str) -> bool: def getVersionWithoutLetters(versionString):
""" return re.sub(r'([A-Za-z]*)', '', versionString)
Check if plugin version is outdated
:param plugin_latest_version: Latest available plugin version
:param plugin_file_version: Installed plugin version
:returns: bool if plugin version is outdated def compareVersions(plugin_latest_version, pluginVersion):
"""
try: try:
plugin_version_tuple = create_plugin_version_tuple( pluginVersionTuple = versionTuple(getVersionWithoutLetters(pluginVersion))
get_plugin_version_without_letters(plugin_file_version)) plugin_latest_versionTuple = versionTuple(getVersionWithoutLetters(plugin_latest_version))
plugin_latest_version_tuple = create_plugin_version_tuple(
get_plugin_version_without_letters(plugin_latest_version))
except ValueError: except ValueError:
return False return False
if plugin_version_tuple < plugin_latest_version_tuple: if pluginVersionTuple < plugin_latest_versionTuple:
return True return True
else: else:
return False return False
def ask_update_confirmation(input_selected_object : str) -> bool: def eggCrackingJar(localJarFileName, searchMode):
""" configValues = configurationValues()
Prints confirmation message of plugins which get updated and ask for confirmation if not configValues.localPluginFolder:
tempPluginFolderPath = createTempPluginFolder()
:param input_selected_object: Command line input if configValues.sftp_useSftp:
sftp = createSFTPConnection()
:returns: True or False if plugins should be udpated pathToPluginJar = Path(f"{tempPluginFolderPath}/{localJarFileName}")
""" sftp_downloadFile(sftp, pathToPluginJar, localJarFileName)
rich_console = Console() else:
rich_console.print("Selected plugins with available Updates:") ftp = createFTPConnection()
for plugin_file in INSTALLEDPLUGINLIST: pathToPluginJar = Path(f"{tempPluginFolderPath}/{localJarFileName}")
if plugin_file.plugin_is_outdated == False: ftp_downloadFile(ftp, pathToPluginJar, localJarFileName)
continue else:
if input_selected_object != "all" and input_selected_object != "*": pluginPath = configValues.pathToPluginFolder
if re.search(input_selected_object, plugin_file.plugin_file_name, re.IGNORECASE): pathToPluginJar = Path(f"{pluginPath}/{localJarFileName}")
rich_console.print(f"[not bold][bright_magenta]{plugin_file.plugin_name}", end=' ') pluginVersion = ''
break pluginName = ''
rich_console.print(f"[not bold][bright_magenta]{plugin_file.plugin_name}", end=' ') with ZipFile(pathToPluginJar, 'r') as pluginJar:
rich_console.print()
update_confirmation = input("Update these plugins [y/n] ? ")
if str.lower(update_confirmation) != "y":
rich_print_error("Aborting the update process")
return False
return True
def egg_cracking_jar(plugin_file_name: str) -> str:
"""
Opens the plugin file as an archive and searches the plugin.yml file for the name and version entry
:param plugin_file_name: Filename of the plugin which should be openend
:returns: Plugin name in plugin.yml file
:returns: Plugin version in plugin.yml file
"""
config_values = config_value()
match config_values.connection:
case "sftp":
path_temp_plugin_folder = create_temp_plugin_folder()
connection = sftp_create_connection()
sftp_download_file(connection, plugin_file_name)
path_plugin_jar = Path(f"{path_temp_plugin_folder}/{plugin_file_name}")
case "ftp":
path_temp_plugin_folder = create_temp_plugin_folder()
connection = ftp_create_connection()
path_plugin_jar = Path(f"{path_temp_plugin_folder}/{plugin_file_name}")
ftp_download_file(connection, path_plugin_jar, plugin_file_name)
case _:
path_plugin_folder = config_values.path_to_plugin_folder
path_plugin_jar = Path(f"{path_plugin_folder}/{plugin_file_name}")
# later used to escape for-loop
plugin_name = plugin_version = ""
# open plugin if it is an archive and read plugin.yml line for line to find name & version
try: try:
with ZipFile(path_plugin_jar, "r") as plugin_jar: with io.TextIOWrapper(pluginJar.open('plugin.yml', 'r'), encoding="utf-8") as pluginYml:
with io.TextIOWrapper(plugin_jar.open("plugin.yml", "r"), encoding="utf-8") as plugin_yml: pluginYmlContentLine = pluginYml.readlines()
for line in plugin_yml: for line in pluginYmlContentLine:
if plugin_name != "" and plugin_version != "": if searchMode == 'version':
break if re.match(r'^\s*?version: ', line):
if re.match(r"^\s*?name: ", line): pluginVersion = re.sub(r'^\s*?version: ', '', line)
plugin_name = re.sub(r'^\s*?name: ', '', line) pluginVersion = pluginVersion.replace('\n', '')
plugin_name = plugin_name.replace("\n", "").replace("'", "").replace('"', "") pluginVersion = pluginVersion.replace("'", '')
pluginVersion = pluginVersion.replace('"', '')
if re.match(r"^\s*?version: ", line): elif searchMode == 'name':
plugin_version = re.sub(r'^\s*?version: ', "", line) if re.match(r'^\s*?name: ', line):
plugin_version = plugin_version.replace("\n", "").replace("'", "").replace('"', "") pluginName = re.sub(r'^\s*?name: ', '', line)
pluginName = pluginName.replace('\n', '')
pluginName = pluginName.replace("'", '')
pluginName = pluginName.replace('"', '')
except FileNotFoundError: except FileNotFoundError:
plugin_name = plugin_version = "" pluginVersion = ''
pluginName = ''
except KeyError: except KeyError:
plugin_name = plugin_version = "" pluginVersion = ''
except zipfile.BadZipFile: pluginName = ''
plugin_name = plugin_version = "" if not configValues.localPluginFolder:
deleteTempPluginFolder(tempPluginFolderPath)
# remove temp plugin folder if plugin was downloaded from sftp/ftp server if searchMode == 'version':
if config_values.connection != "local": return pluginVersion
remove_temp_plugin_folder() if searchMode == 'name':
return pluginName
return plugin_name, plugin_version
def check_update_available_installed_plugins(input_selected_object: str, config_values: config_value) -> str: def checkInstalledPackage(inputSelectedObject="all", inputOptionalParam=None):
""" configValues = configurationValues()
Gets installed plugins and checks it against the apis if there are updates for the plugins available createPluginList()
pluginFolderPath = configValues.pathToPluginFolder
if not configValues.localPluginFolder:
if configValues.sftp_useSftp:
connection = createSFTPConnection()
pluginList = sftp_listAll(connection)
else:
connection = createFTPConnection()
pluginList = ftp_listAll(connection)
else:
pluginList = os.listdir(pluginFolderPath)
:param input_selected_object: Command line input (default: all) i = 0
:param config_values: Config values from config file oldPlugins = 0
print(oColors.brightBlack + f"Checking: {inputSelectedObject}" + oColors.standardWhite)
:returns: Count of plugins, Count of plugins with available updates if inputOptionalParam != "changelog":
""" print(oColors.brightBlack + f"Use 'check {inputSelectedObject} changelog' to get the latest changelog from plugins" + oColors.standardWhite)
Plugin.create_plugin_list() print("┌─────┬────────────────────────────────┬──────────────┬──────────────┐")
match config_values.connection: print("│ No. │ Name │ Installed V. │ Latest V. │")
case "sftp": print("└─────┴────────────────────────────────┴──────────────┴──────────────┘")
connection = sftp_create_connection()
plugin_list = sftp_list_all(connection)
case "ftp":
connection = ftp_create_connection()
plugin_list = ftp_list_all(connection)
case _:
plugin_folder_path = config_values.path_to_plugin_folder
plugin_list = os.listdir(plugin_folder_path)
plugin_count = plugins_with_udpates = 0
# create simple progress bar from rich
for plugin_file in track(plugin_list, description="[cyan]Checking...", transient=True, style="bright_yellow"):
plugin_attributes = True
match config_values.connection:
case "sftp":
plugin_attributes = sftp_validate_file_attributes(
connection, f"{config_values.remote_plugin_folder_on_server}/{plugin_file}"
)
case "ftp":
plugin_attributes = ftp_validate_file_attributes(
connection, f"{config_values.remote_plugin_folder_on_server}/{plugin_file}"
)
case _:
if not os.path.isfile(Path(f"{plugin_folder_path}/{plugin_file}")):
plugin_attributes = False
if not re.search(r'.jar$', plugin_file):
plugin_attributes = False
# skip plugin if no attributes were found to skip not valid plugin files
if plugin_attributes == False:
continue
plugin_file_name = get_plugin_file_name(plugin_file)
# supports command 'check pluginname' and skip the checking of every other plugin to speed things up a bit
if input_selected_object != "all" and input_selected_object != "*":
if not re.search(input_selected_object, plugin_file_name, re.IGNORECASE):
continue
plugin_file_version = get_plugin_file_version(plugin_file)
# check repository of plugin
plugin_spigot_id = search_plugin_spiget(plugin_file, plugin_file_name, plugin_file_version) # plugin_spigot_id isn't needed
# TODO add more plugin repositories here
# plugin wasn't found and not added to global plugin list so add
try: try:
if plugin_file not in INSTALLEDPLUGINLIST[-1].plugin_file_name: for plugin in track(pluginList, description="Checking for updates" ,transient=True, complete_style="bright_yellow"):
Plugin.add_to_plugin_list(plugin_file, plugin_file_name, plugin_file_version, 'N/A', False, 'N/A', ()) if not configValues.localPluginFolder:
pluginFile = f"{configValues.sftp_folderPath}/{plugin}"
if configValues.sftp_useSftp:
pluginAttributes = sftp_validateFileAttributes(connection, pluginFile)
if pluginAttributes == False:
continue
else:
pluginAttributes = ftp_validateFileAttributes(connection, pluginFile)
if pluginAttributes == False:
continue
else:
if not os.path.isfile(Path(f"{pluginFolderPath}/{plugin}")):
continue
if not re.search(r'.jar$', plugin):
continue
try:
fileName = getFileName(plugin)
fileVersion = getFileVersion(plugin)
pluginId = getInstalledPlugin(fileName, fileVersion, plugin)
except TypeError:
continue
pluginIdStr = str(pluginId)
if fileVersion == '':
fileVersion = 'N/A'
try:
pluginLatestVersion = INSTALLEDPLUGINLIST[i][3]
except IndexError: except IndexError:
Plugin.add_to_plugin_list(plugin_file, plugin_file_name, plugin_file_version, 'N/A', False, 'N/A', ()) pluginLatestVersion = 'N/A'
if INSTALLEDPLUGINLIST[-1].plugin_is_outdated == True:
plugins_with_udpates += 1
plugin_count += 1
return plugin_count, plugins_with_udpates
if pluginLatestVersion == None:
pluginLatestVersion = 'N/A'
def check_installed_plugins(input_selected_object : str="all", input_parameter : str=None) -> None: try:
""" pluginIsOutdated = INSTALLEDPLUGINLIST[i][4]
Prints table overview of installed plugins with versions and available updates except IndexError:
pluginIsOutdated = 'N/A'
:param input_selected_object: Which plugin should be checked if pluginIsOutdated == None:
:param input_parameter: Optional parameters pluginIsOutdated = 'N/A'
:returns: None if pluginIsOutdated == True:
""" oldPlugins = oldPlugins + 1
config_values = config_value()
plugin_count, plugins_with_udpates = check_update_available_installed_plugins(input_selected_object, config_values)
# print rich table of found plugins and result if re.search(r'.jar$', fileName):
rich_table = Table(box=None) fileName = eggCrackingJar(plugin, "name")
rich_table.add_column("No.", justify="right", style="cyan", no_wrap=True)
rich_table.add_column("Name", style="bright_magenta") if inputSelectedObject != "all" and inputSelectedObject != "*":
rich_table.add_column("Installed V.", justify="right", style="green") if inputSelectedObject != pluginIdStr or not re.search(inputSelectedObject, fileName, re.IGNORECASE):
rich_table.add_column("Latest V.", justify="right", style="bright_green")
rich_table.add_column("Update available", justify="left", style="white")
rich_table.add_column("Repository", justify="left", style="white")
# start counting at 1 for all my non-programming friends :)
i = 1
for plugin in INSTALLEDPLUGINLIST:
rich_table.add_row(
str(i),
plugin.plugin_name,
plugin.plugin_file_version,
plugin.plugin_latest_version,
str(plugin.plugin_is_outdated),
plugin.plugin_repository
)
i += 1 i += 1
continue
rich_console = Console() if inputSelectedObject == "all" or inputSelectedObject != "*" or inputSelectedObject != "all":
rich_console.print(rich_table) if inputSelectedObject == pluginIdStr or re.search(inputSelectedObject, fileName, re.IGNORECASE):
rich_console.print() if pluginLatestVersion == 'N/A':
if plugins_with_udpates != 0: print(oColors.brightBlack + f" [{1}]".rjust(6), end='')
rich_console.print(
"[not bold][bright_yellow]Plugins with available updates: [bright_green]" +
f"{plugins_with_udpates}[bright_yellow]/[green]{plugin_count}"
)
else: else:
rich_console.print(f"[bright_green]All found plugins are on the newest version!") print(f" [{1}]".rjust(6), end='')
return None else:
if pluginLatestVersion == 'N/A':
print(oColors.brightBlack + f" [{i+1}]".rjust(6), end='')
else:
print(f" [{i+1}]".rjust(6), end='')
print(" ", end='')
if pluginIsOutdated == True:
print(oColors.brightRed + f"{fileName}".ljust(33) + oColors.standardWhite, end='')
elif pluginIsOutdated == False:
print(oColors.brightGreen + f"{fileName}".ljust(33) + oColors.standardWhite, end='')
else:
print(f"{fileName}".ljust(33), end='')
print(f"{fileVersion}".ljust(15), end='')
print(f"{pluginLatestVersion}".ljust(15))
if (inputOptionalParam == "changelog" and pluginLatestVersion != 'N/A'):
print(oColors.brightYellow + f"CHANGELOG {fileName}:" + oColors.standardWhite)
description = getUpdateDescription(pluginId)
print(description)
print()
if inputSelectedObject == pluginIdStr or re.search(inputSelectedObject, fileName, re.IGNORECASE):
break
else:
print(oColors.brightRed + "Wrong input! Use 'check all' to check every plugin for updates!" + oColors.standardWhite)
break
i += 1
except TypeError:
print(oColors.brightRed + "Error occured: Aborted checking for updates." + oColors.standardWhite)
print(oColors.brightYellow + f"Outdated plugins: [{oldPlugins}/{i}]" + oColors.standardWhite)
def update_installed_plugins(input_selected_object : str="all", no_confirmation : bool=False) -> None: def updateInstalledPackage(inputSelectedObject='all'):
""" configValues = configurationValues()
Checks if a plugin list exists and if so updates the selected plugins if there is an update available if not configValues.localPluginFolder:
if configValues.sftp_useSftp:
connection = createSFTPConnection()
else:
connection = createFTPConnection()
:param input_selected_object: Plugin name to update (use 'all' or '*' for everything)
:param no_confirmation: Don't ask for confirmation if pluGET was called with param: --no-confirmation
:returns: None
"""
rich_console = Console()
config_values = config_value()
match config_values.connection:
case "sftp":
connection = sftp_create_connection()
case "ftp":
connection = ftp_create_connection()
# if INSTALLEDPLUGINLIST was not previously filled by 'check' command call the command to fill plugin list
try: try:
if len(INSTALLEDPLUGINLIST) == 0: print(oColors.brightBlack + "Selected plugins with available Updates:" + oColors.standardWhite)
check_update_available_installed_plugins(input_selected_object, config_values) if inputSelectedObject == "all" or inputSelectedObject == "*":
for pluginIndex in range(len(INSTALLEDPLUGINLIST)):
if INSTALLEDPLUGINLIST[pluginIndex][4] == True:
fileName = getFileName(INSTALLEDPLUGINLIST[pluginIndex][0])
print(fileName, end=' ')
else:
print(inputSelectedObject, end=' ')
print()
updateConfirmation = input("Update these plugins [y/n] ? ")
if str.lower(updateConfirmation) != "y":
print(oColors.brightRed + "Aborting the update process."+ oColors.standardWhite)
return False
except NameError: except NameError:
check_update_available_installed_plugins(input_selected_object, config_values) print(oColors.brightRed + "Check for updates before updating plugins with: 'check all'" + oColors.standardWhite)
print(oColors.brightRed + "Started checking for updates..." + oColors.standardWhite)
checkInstalledPackage()
print(oColors.brightRed + f"Please input 'update {inputSelectedObject}' again!" + oColors.standardWhite)
return False
# if argument 'all' was given recheck all plugins to avoid having only a few plugins from previously cached checks i = 0
if input_selected_object == "all" or input_selected_object == "*": pluginsUpdated = 0
check_update_available_installed_plugins(input_selected_object, config_values) indexNumberUpdated = 0
print(oColors.brightBlack + f"Updating: {inputSelectedObject}" + oColors.standardWhite)
# skip confirmation message if pluGET was called with --no-confirmation print("┌─────┬────────────────────────────────┬────────────┬──────────┐")
if no_confirmation == False: print("│ No. │ Name │ Old V. │ New V. │")
if ask_update_confirmation(input_selected_object) == False: print("└─────┴────────────────────────────────┴────────────┴──────────┘")
return None try:
for pluginArray in track(INSTALLEDPLUGINLIST, description="Updating" ,transient=True, complete_style="bright_magenta", ):
# used later for output as stats plugin = INSTALLEDPLUGINLIST[i][0]
plugins_updated = plugins_skipped = 0 if not configValues.localPluginFolder:
pluginFile = f"{configValues.sftp_folderPath}/{plugin}"
#for plugin in track(INSTALLEDPLUGINLIST, description="[cyan]Updating...", transient=True, style="bright_yellow"): if configValues.sftp_useSftp:
for plugin in INSTALLEDPLUGINLIST: pluginAttributes = sftp_validateFileAttributes(connection, pluginFile)
# supports command 'update pluginname' and skip the updating of every other plugin to speed things up a bit if pluginAttributes == False:
if input_selected_object != "all" and input_selected_object != "*": i += 1
if not re.search(input_selected_object, plugin.plugin_file_name, re.IGNORECASE):
plugins_skipped += 1
continue continue
if plugin.plugin_is_outdated == False:
plugins_skipped += 1
continue
rich_console.print(
"\n [not bold][bright_white]● [bright_magenta]" +
f"{plugin.plugin_name} [green]{plugin.plugin_file_version}" + \
f" [cyan]→ [bright_green]{plugin.plugin_latest_version}"
)
plugins_updated += 1
plugin_path = get_download_path(config_values)
match config_values.connection:
# local plugin folder
case "local":
match (plugin.plugin_repository):
case "spigot":
try:
get_specific_plugin_spiget(plugin.plugin_repository_data[0])
except HTTPError as err:
rich_print_error(f"HTTPError: {err.code} - {err.reason}")
plugins_updated -= 1
except TypeError:
rich_print_error(
f"Error: TypeError > Couldn't download new version. Is the file available on spigotmc?"
)
plugins_updated -= 1
case _:
rich_print_error("Error: Plugin repository wasn't found")
return None
# don't delete files if they are downloaded to a seperate download path
if config_values.local_seperate_download_path == False:
try:
os.remove(Path(f"{plugin_path}/{plugin.plugin_file_name}"))
rich_console.print(
" [not bold][bright_green]Deleted old plugin file [cyan]→ [white]" +
f"{plugin.plugin_file_name}"
)
except FileNotFoundError:
rich_print_error("Error: Old plugin file couldn't be deleted")
# plugin folder is on sftp or ftp server
case _:
plugin_path = f"{plugin_path}/{plugin.plugin_file_name}"
match (plugin.plugin_repository):
case "spigot":
try:
get_specific_plugin_spiget(plugin.plugin_repository_data[0])
except HTTPError as err:
rich_print_error(f"HTTPError: {err.code} - {err.reason}")
plugins_updated -= 1
except TypeError:
rich_print_error(
f"Error: TypeError > Couldn't download new version. Is the file available on spigotmc?"
)
plugins_updated -= 1
case _:
rich_print_error("Error: Plugin repository wasn't found")
return None
# don't delete old plugin files if they are downloaded to a seperate download path
if config_values.remote_seperate_download_path == False:
match config_values.connection:
case "sftp":
try:
connection.remove(plugin_path)
rich_console.print(
" [not bold][bright_green]Deleted old plugin file [cyan]→ [white]" +
f"{plugin.plugin_file_name}"
)
except FileNotFoundError:
rich_print_error("Error: Old plugin file couldn't be deleted")
case "ftp":
try:
connection.delete(plugin_path)
rich_console.print(
" [not bold][bright_green]Deleted old plugin file [cyan]→ [white]" +
f"{plugin.plugin_file_name}"
)
except FileNotFoundError:
rich_print_error("Error: Old plugin file couldn't be deleted")
rich_console.print(
f"\n[not bold][bright_green]Plugins updated: {plugins_updated}/{(len(INSTALLEDPLUGINLIST) - plugins_skipped)}"
)
return None
def search_plugin_spiget(plugin_file: str, plugin_file_name: str, plugin_file_version: str) -> int:
"""
Search the spiget api for the installed plugin and add it to the installed plugin list
:param plugin_file: Full file name of plugin
:param plugin_file_name: Name of plugin file
:param plugin_file_version: Version of plugin file
:returns: Plugin ID of Spigot Plugin
"""
url = f"https://api.spiget.org/v2/search/resources/{plugin_file_name}?field=name&sort=-downloads"
plugin_list = api_do_request(url)
# Handle failed api request
"""
{'error': 'Unexpected Exception', 'msg': 'Unexpected Exception. Please report this to
https://github.com/SpiGetOrg/api.spiget.org/issues'}
"""
if "error" in plugin_list:
rich_print_error(
f"[not bold]Error: Spiget error occurred whilst searching for plugin '{plugin_file}': {plugin_list['msg']}"
)
return plugin_list['msg']
else: else:
plugin_file_version2 = plugin_file_version pluginAttributes = ftp_validateFileAttributes(connection, pluginFile)
for i in range(4): if pluginAttributes == False:
i += 1
continue
else:
pluginFolderPath = configValues.pathToPluginFolder
if not os.path.isfile(Path(f"{pluginFolderPath}/{plugin}")):
i += 1
continue
if not re.search(r'.jar$', plugin):
i += 1
continue
try:
fileName = getFileName(plugin)
fileVersion = getFileVersion(plugin)
pluginId = INSTALLEDPLUGINLIST[i][1]
latestVersion = INSTALLEDPLUGINLIST[i][3]
except (TypeError, ValueError):
i += 1
continue
if re.search(r'.jar$', fileName):
fileName = eggCrackingJar(plugin, "name")
pluginIdStr = str(pluginId)
if pluginId == None or pluginId == '':
i += 1
continue
if inputSelectedObject != 'all' and inputSelectedObject != pluginIdStr and not re.search(inputSelectedObject, fileName, re.IGNORECASE):
i += 1
continue
if INSTALLEDPLUGINLIST[i][4] != True:
i += 1
continue
if INSTALLEDPLUGINLIST[i][4] == False and inputSelectedObject != 'all':
print(oColors.brightGreen + f"{fileName} is already on {latestVersion}" + oColors.standardWhite)
print(oColors.brightRed + "Aborting the update process."+ oColors.standardWhite)
break
print(f" [{indexNumberUpdated+1}]".rjust(6), end='')
print(" ", end='')
print(f"{fileName}".ljust(33), end='')
print(f"{fileVersion}".ljust(13), end='')
print(f"{latestVersion}".ljust(13))
if not configValues.localPluginFolder:
if configValues.sftp_seperateDownloadPath is True:
pluginPath = configValues.sftp_pathToSeperateDownloadPath
else:
pluginPath = configValues.sftp_folderPath
pluginPath = f"{pluginPath}/{plugin}"
indexNumberUpdated += 1
pluginsUpdated += 1
if configValues.sftp_useSftp:
sftp = createSFTPConnection()
try:
getSpecificPackage(pluginId, pluginPath)
if configValues.sftp_seperateDownloadPath is False:
sftp.remove(pluginPath)
except HTTPError as err:
print(oColors.brightRed + f"HTTPError: {err.code} - {err.reason}" + oColors.standardWhite)
pluginsUpdated -= 1
except TypeError:
print(oColors.brightRed + f"TypeError: Couldn't download new version. Is the file available on spigotmc?" + oColors.standardWhite)
pluginsUpdated -= 1
except FileNotFoundError:
print(oColors.brightRed + f"FileNotFoundError: Old plugin file coulnd't be deleted" + oColors.standardWhite)
else:
ftp = createFTPConnection()
try:
getSpecificPackage(pluginId, pluginPath)
if configValues.sftp_seperateDownloadPath is False:
ftp.delete(pluginPath)
except HTTPError as err:
print(oColors.brightRed + f"HTTPError: {err.code} - {err.reason}" + oColors.standardWhite)
pluginsUpdated -= 1
except TypeError:
print(oColors.brightRed + f"TypeError: Couldn't download new version. Is the file available on spigotmc?" + oColors.standardWhite)
pluginsUpdated -= 1
except FileNotFoundError:
print(oColors.brightRed + f"FileNotFoundError: Old plugin file coulnd't be deleted" + oColors.standardWhite)
else:
if configValues.seperateDownloadPath is True:
pluginPath = configValues.pathToSeperateDownloadPath
else:
pluginPath = configValues.pathToPluginFolder
indexNumberUpdated += 1
pluginsUpdated += 1
try:
getSpecificPackage(pluginId, pluginPath)
if configValues.seperateDownloadPath is False:
pluginPath = f"{pluginPath}/{plugin}"
os.remove(pluginPath)
except HTTPError as err:
print(oColors.brightRed + f"HTTPError: {err.code} - {err.reason}" + oColors.standardWhite)
pluginsUpdated -= 1
except TypeError:
print(oColors.brightRed + f"TypeError: Couldn't download new version. Is the file available on spigotmc?" + oColors.standardWhite)
pluginsUpdated -= 1
except FileNotFoundError:
print(oColors.brightRed + f"FileNotFoundError: Old plugin file coulnd't be deleted" + oColors.standardWhite)
if inputSelectedObject != 'all':
break
elif inputSelectedObject != 'all':
print(oColors.brightGreen + f"{fileName} is already on {latestVersion}" + oColors.standardWhite)
print(oColors.brightRed + "Aborting the update process."+ oColors.standardWhite)
break
i += 1
except TypeError:
print(oColors.brightRed + "Error occured: Aborted updating plugins." + oColors.standardWhite)
except NameError:
print(oColors.brightRed + "Check for updates before updating plugins with: 'check all'" + oColors.standardWhite)
print(oColors.brightRed + "Started checking for updates..." + oColors.standardWhite)
checkInstalledPackage()
print(oColors.brightRed + f"Please input 'update {inputSelectedObject}' again!" + oColors.standardWhite)
if i != 0:
print(oColors.brightYellow + f"Plugins updated: [{pluginsUpdated}/{i}]" + oColors.standardWhite)
if inputSelectedObject =='all' and pluginsUpdated == 0 and i != 0:
print(oColors.brightGreen + "All found plugins are on the latest version!" + oColors.standardWhite)
def getInstalledPlugin(localFileName, localFileVersion, localPluginFullName):
url = "https://api.spiget.org/v2/search/resources/" + localFileName + "?field=name&sort=-downloads"
packageName = doAPIRequest(url)
plugin_match_found = False
pluginID = None
localFileVersionNew = localFileVersion
i = 0
for i in range(0, 3):
if plugin_match_found == True:
break
if i == 1: if i == 1:
plugin_file_version2 = re.sub(r'(\-\w*)', '', plugin_file_version) localFileVersionNew = re.sub(r'(\-\w*)', '', localFileVersion)
if i == 2: if i == 2:
plugin_name_in_yml, plugin_version_in_yml = egg_cracking_jar(plugin_file) pluginNameinYML = eggCrackingJar(localPluginFullName, 'name')
url = f"https://api.spiget.org/v2/search/resources/{plugin_name_in_yml}?field=name&sort=-downloads" url = "https://api.spiget.org/v2/search/resources/" + pluginNameinYML + "?field=name&sort=-downloads"
try: try:
plugin_list = api_do_request(url) packageName = doAPIRequest(url)
except ValueError: except ValueError:
continue continue
# if no plugin name was found with egg_cracking_jar() skip this round
if plugin_list is None: localFileVersion = localFileVersionNew
for resource in packageName:
if plugin_match_found == True:
continue continue
pID = resource["id"]
# search with version which is in plugin.yml for the plugin url2 = f"https://api.spiget.org/v2/resources/{pID}/versions?size=100&sort=-name"
if i == 3:
plugin_file_version2 = plugin_version_in_yml
for plugin in plugin_list:
plugin_id = plugin["id"]
url2 = f"https://api.spiget.org/v2/resources/{plugin_id}/versions?size=100&sort=-name"
try: try:
plugin_versions = api_do_request(url2) packageVersions = doAPIRequest(url2)
except ValueError: except ValueError:
continue continue
if plugin_versions is None: for updates in packageVersions:
continue updateVersion = updates["name"]
for updates in plugin_versions: if localFileVersionNew in updateVersion:
update_version_name = updates["name"] plugin_match_found = True
if plugin_file_version2 in update_version_name: pluginID = pID
#spigot_update_id = updates["id"] updateId = updates["id"]
plugin_latest_version = get_latest_plugin_version_spiget(plugin_id) plugin_latest_version = getLatestPluginVersion(pID)
plugin_is_outdated = compare_plugin_version(plugin_latest_version, update_version_name) plugin_is_outdated = compareVersions(plugin_latest_version, updateVersion)
Plugin.add_to_plugin_list( addToPluginList(localPluginFullName, pID, updateId, plugin_latest_version , plugin_is_outdated)
plugin_file, return pluginID
plugin_file_name,
plugin_file_version, else:
plugin_latest_version, if plugin_match_found != True:
plugin_is_outdated, pID = updateId = plugin_latest_version = plugin_is_outdated = None
"spigot", addToPluginList(localPluginFullName, pID, updateId, plugin_latest_version , plugin_is_outdated)
[plugin_id]
) return pluginID
return plugin_id
return None

View File

@ -0,0 +1,114 @@
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

@ -0,0 +1,218 @@
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

@ -1,277 +0,0 @@
"""
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

@ -1,208 +0,0 @@
"""
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

@ -1,139 +0,0 @@
"""
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

View File

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

View File

View File

@ -1,31 +0,0 @@
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

@ -1,61 +0,0 @@
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

@ -1,42 +0,0 @@
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

@ -1,40 +0,0 @@
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()

View File

@ -1,152 +0,0 @@
"""
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

@ -0,0 +1,89 @@
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,210 +1,175 @@
""" # misc functions
Holds all the utilitie code for pluGET and the webrequests function
"""
import os import os
import sys import sys
import requests
import shutil import shutil
import re import requests
from pathlib import Path 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 src.utils.console_output import rich_print_error from utils.consoleoutput import oColors
from src.handlers.handle_config import config_value from handlers.handle_config import configurationValues
from src.settings import PLUGETVERSION from handlers.handle_sftp import createSFTPConnection
from handlers.handle_ftp import createFTPConnection
def get_command_help(command: str) -> None: def getHelp():
""" print(oColors.brightYellow+ "Need help?" + oColors.standardWhite)
Prints the help page for all commands and individual commands print("For a list of all commands: 'help command'")
print("Or check the docs here:")
:param command: Console command which the help page should show print("https://github.com/Neocky/pluGET")
print("Or go to the official discord.")
:returns: None print("The link for discord can also be found on Github!")
"""
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
def check_for_pluGET_update() -> None: if optionalParams == 'exit':
""" print(oColors.brightBlack + " GENERAL:" + oColors.standardWhite)
Check with the github api if there is a new version for pluGET available and print download message if this is print(" exit ./anything Exit pluGET")
the case break
"""
response = api_do_request("https://api.github.com/repos/Neocky/pluGET/releases/latest") if optionalParams == 'help':
# get '.1.6.10' as output print(oColors.brightBlack + " GENERAL:" + oColors.standardWhite)
full_version_string = re.search(r"[\.?\d]*$", response["name"]) print(" help ./anything Get general help")
# remove '.' to get '1.6.10' as output print(" help command all/command Get specific help to the commands of pluGET")
version = re.sub(r"^\.*", "", full_version_string.group()) break
console = Console()
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: try:
pluget_installed_version_tuple = tuple(map(int, (PLUGETVERSION.split(".")))) os.mkdir(pluginFolderPath)
plugin_latest_version_tuple = tuple(map(int, (version.split(".")))) except OSError:
except ValueError: print(oColors.brightRed + "Creation of directory %s failed" % pluginFolderPath)
console.print("Couldn't check if new version of pluGET is available") print(oColors.brightRed + "Please check the config file!" + oColors.standardWhite)
return None input("Press any key + enter to exit...")
if pluget_installed_version_tuple < plugin_latest_version_tuple: sys.exit()
print(f"A new version of pluGET is available: {version}") else:
console.print("Download it here: ", end='') print("Created directory %s" % pluginFolderPath)
console.print("https://github.com/Neocky/pluGET", style="link https://github.com/Neocky/pluGET")
return None
def api_do_request(url) -> list: def apiTest():
""" apiStatusUrl = 'https://api.spiget.org/v2/status'
Handles the webrequest and returns a json list
"""
webrequest_header = {'user-agent': 'pluGET/1.0'}
try: try:
response = requests.get(url, headers=webrequest_header) r = requests.get(apiStatusUrl)
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): except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError):
rich_print_error("Error: Couldn't make a connection to the API. Check you connection to the internet!") 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...")
sys.exit() sys.exit()
if r.status_code != 200: if r.status_code != 200:
rich_print_error("Error: Problems with the API detected. Plese try it again later!") print(oColors.brightRed + "Problems with the API detected. Plese try it again later!" + oColors.standardWhite)
input("Press any key + enter to exit...")
sys.exit() sys.exit()
return None
def create_temp_plugin_folder() -> Path: def check_requirements():
""" configValues = configurationValues()
Creates a temporary folder to store plugins inside apiTest()
Returns full path of temporary folder check_local_plugin_folder()
""" if not configValues.localPluginFolder:
path_temp_plugin_folder = Path("./TempSFTPFolder") if configValues.sftp_useSftp:
if os.path.isdir(path_temp_plugin_folder): createSFTPConnection()
return path_temp_plugin_folder
try:
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:
rich_print_error(f"Error: {e.filename} - {e.strerror}")
return
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 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: else:
plugin_folder_path = config_values.path_to_plugin_folder createFTPConnection()
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!") 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() sys.exit()
return None return tempPluginFolder
def check_requirements() -> None: def deleteTempPluginFolder(tempPluginFolder):
""" try:
Check if the plugin folders are available shutil.rmtree(tempPluginFolder)
""" except OSError as e:
config_values = config_value() print ("Error: %s - %s." % (e.filename, e.strerror))
match config_values.connection:
case "local":
check_local_plugin_folder(config_values) def calculateFileSizeMb(downloadFileSize):
case "sftp": fileSizeDownload = int(downloadFileSize)
sftp_create_connection() fileSizeMb = fileSizeDownload / 1024 / 1024
case "ftp": roundedFileSize = round(fileSizeMb, 2)
ftp_create_connection() return roundedFileSize
return None
def calculateFileSizeKb(downloadFileSize):
fileSizeDownload = int(downloadFileSize)
fileSizeKb = fileSizeDownload / 1024
roundedFileSize = round(fileSizeKb, 2)
return roundedFileSize

9
src/utils/web_request.py Normal file
View File

@ -0,0 +1,9 @@
# 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