mirror of
https://github.com/Neocky/pluGET.git
synced 2024-04-29 16:12:30 +00:00
Split everything in sub packages; Created main function; Added many exception handlers for SFTP
Added: - Split everything into sub packages - created a main file: __main__.py - added many exception handlings with SFTP Edited: - launcher.bat calls now the __main__.py file
This commit is contained in:
parent
36c96f7d23
commit
ac561d92ce
@ -1,2 +1,2 @@
|
|||||||
@ECHO OFF
|
@ECHO OFF
|
||||||
py "%~dp0\src\handle_input.py"
|
py "%~dp0\src\__main__.py"
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
20
src/__main__.py
Normal file
20
src/__main__.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from utils.consoleoutput import consoleTitle, clearConsole, printMainMenu, oColors
|
||||||
|
from utils.utilities import getHelp, check_requirements
|
||||||
|
from handlers.handle_input import createInputLists, getInput, outputTest
|
||||||
|
from handlers.handle_config import checkConfig
|
||||||
|
from plugin.plugin_downloader import searchPackage, getSpecificPackage
|
||||||
|
from plugin.plugin_updatechecker import updateInstalledPackage, checkInstalledPackage
|
||||||
|
from plugin.plugin_remover import removePlugin
|
||||||
|
|
||||||
|
|
||||||
|
def mainFunction():
|
||||||
|
consoleTitle()
|
||||||
|
clearConsole()
|
||||||
|
checkConfig()
|
||||||
|
check_requirements()
|
||||||
|
createInputLists()
|
||||||
|
printMainMenu()
|
||||||
|
getInput()
|
||||||
|
#outputTest()
|
||||||
|
|
||||||
|
mainFunction()
|
@ -1,41 +0,0 @@
|
|||||||
import sys
|
|
||||||
import pysftp
|
|
||||||
from handle_config import checkConfig
|
|
||||||
from consoleoutput import oColors
|
|
||||||
|
|
||||||
def createSFTPConnection():
|
|
||||||
cnopts = pysftp.CnOpts()
|
|
||||||
cnopts.hostkeys = None # TODO fix this
|
|
||||||
sftp = pysftp.Connection(checkConfig().sftp_server, username=checkConfig().sftp_user, password=checkConfig().sftp_password, port=checkConfig().sftp_port, cnopts=cnopts)
|
|
||||||
return sftp
|
|
||||||
|
|
||||||
def sftp_showPlugins(sftp):
|
|
||||||
sftp.cd('plugins')
|
|
||||||
for attr in sftp.listdir_attr():
|
|
||||||
print(attr.filename, attr)
|
|
||||||
|
|
||||||
def sftp_cdPluginDir(sftp):
|
|
||||||
sftp.cd('plugins')
|
|
||||||
|
|
||||||
|
|
||||||
def sftp_upload_file(sftp, itemPath):
|
|
||||||
#sftp = createSFTPConnection()
|
|
||||||
try:
|
|
||||||
sftp.chdir('plugins')
|
|
||||||
sftp.put(itemPath)
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(oColors.brightRed + "The *plugins* folder couldn*t be found on the remote host!" + oColors.standardWhite)
|
|
||||||
print(oColors.brightRed + "Aborting installation." + oColors.standardWhite)
|
|
||||||
sftp.close()
|
|
||||||
|
|
||||||
|
|
||||||
def sftp_listAll(sftp):
|
|
||||||
try:
|
|
||||||
sftp.chdir('plugins')
|
|
||||||
installedPlugins = sftp.listdir()
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(oColors.brightRed + "The *plugins* folder couldn*t be found on the remote host!" + oColors.standardWhite)
|
|
||||||
|
|
||||||
return installedPlugins
|
|
0
src/handlers/__init__.py
Normal file
0
src/handlers/__init__.py
Normal file
@ -1,9 +1,8 @@
|
|||||||
# handles the config and everything around it
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import configparser
|
import configparser
|
||||||
import os.path
|
|
||||||
|
|
||||||
from consoleoutput import oColors
|
from utils.consoleoutput import oColors
|
||||||
|
|
||||||
|
|
||||||
def checkConfig():
|
def checkConfig():
|
||||||
@ -21,11 +20,11 @@ def checkConfig():
|
|||||||
localPluginFolder = config['General']['LocalPluginFolder']
|
localPluginFolder = config['General']['LocalPluginFolder']
|
||||||
pathToPluginFolder = config['General']['PathToPluginFolder']
|
pathToPluginFolder = config['General']['PathToPluginFolder']
|
||||||
|
|
||||||
sftp_server = config['Remote Server']['Server']
|
sftp_server = config['SFTP - Remote Server']['Server']
|
||||||
sftp_user = config['Remote Server']['Username']
|
sftp_user = config['SFTP - Remote Server']['Username']
|
||||||
sftp_password = config['Remote Server']['Password']
|
sftp_password = config['SFTP - Remote Server']['Password']
|
||||||
sftp_port = config['Remote Server']['Port']
|
sftp_port = config['SFTP - Remote Server']['Port']
|
||||||
sftp_folderPath = config['Remote Server']['PluginFolder']
|
sftp_folderPath = config['SFTP - Remote Server']['PluginFolderForUpload']
|
||||||
|
|
||||||
sftp_port = int(sftp_port)
|
sftp_port = int(sftp_port)
|
||||||
if localPluginFolder == 'True':
|
if localPluginFolder == 'True':
|
||||||
@ -39,15 +38,16 @@ def checkConfig():
|
|||||||
def createConfig():
|
def createConfig():
|
||||||
config = configparser.ConfigParser(allow_no_value=True)
|
config = configparser.ConfigParser(allow_no_value=True)
|
||||||
config['General'] = {}
|
config['General'] = {}
|
||||||
config['General'][';'] = 'If a local plugin folder exists (True/False): (If false use sftp)'
|
config['General'][';'] = 'If a local plugin folder exists (True/False): (If False use SFTP)'
|
||||||
config['General']['LocalPluginFolder'] = 'True'
|
config['General']['LocalPluginFolder'] = 'True'
|
||||||
config['General']['PathToPluginFolder'] = 'C:\\Users\\USER\\Desktop\\plugins'
|
config['General']['PathToPluginFolder'] = 'C:\\Users\\USER\\Desktop\\plugins'
|
||||||
config['Remote Server'] = {}
|
config['SFTP - Remote Server'] = {}
|
||||||
config['Remote Server']['Server'] = '0.0.0.0'
|
config['SFTP - Remote Server']['Server'] = '0.0.0.0'
|
||||||
config['Remote Server']['Username'] = 'user'
|
config['SFTP - Remote Server']['Username'] = 'user'
|
||||||
config['Remote Server']['Password'] = 'longpassword'
|
config['SFTP - Remote Server']['Password'] = 'longpassword'
|
||||||
config['Remote Server']['Port'] = '22'
|
config['SFTP - Remote Server'][';'] = 'Normally you won*t need to change anything below this line'
|
||||||
config['Remote Server']['PluginFolder'] = '.\\plugins'
|
config['SFTP - Remote Server']['Port'] = '22'
|
||||||
|
config['SFTP - Remote Server']['PluginFolderForUpload'] = '.\\plugins'
|
||||||
|
|
||||||
with open('./config.ini', 'w') as configfile:
|
with open('./config.ini', 'w') as configfile:
|
||||||
config.write(configfile)
|
config.write(configfile)
|
@ -1,12 +1,12 @@
|
|||||||
import time
|
|
||||||
import sys
|
import sys
|
||||||
from consoleoutput import consoleTitle, clearConsole, printMainMenu, oColors
|
import time
|
||||||
from plugin_downloader import searchPackage, getSpecificPackage
|
|
||||||
from plugin_updatechecker import updateInstalledPackage, checkInstalledPackage
|
from utils.consoleoutput import oColors
|
||||||
from handle_config import checkConfig
|
from utils.utilities import getHelp
|
||||||
from utilities import getHelp, check_requirements
|
from handlers.handle_config import checkConfig
|
||||||
from handle_sftp import createSFTPConnection, sftp_showPlugins
|
from plugin.plugin_downloader import searchPackage, getSpecificPackage
|
||||||
from plugin_remover import removePlugin
|
from plugin.plugin_updatechecker import updateInstalledPackage, checkInstalledPackage
|
||||||
|
from plugin.plugin_remover import removePlugin
|
||||||
|
|
||||||
def createInputLists():
|
def createInputLists():
|
||||||
global COMMANDLIST
|
global COMMANDLIST
|
||||||
@ -34,6 +34,7 @@ def handleInput(inputCommand, inputSelectedObject, inputParams):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
getSpecificPackage(inputSelectedObject, checkConfig().pathToPluginFolder, inputParams)
|
getSpecificPackage(inputSelectedObject, checkConfig().pathToPluginFolder, inputParams)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
searchPackage(inputSelectedObject)
|
searchPackage(inputSelectedObject)
|
||||||
break
|
break
|
||||||
@ -72,17 +73,7 @@ def getInput():
|
|||||||
handleInput(inputCommand, inputSelectedObject, inputParams)
|
handleInput(inputCommand, inputSelectedObject, inputParams)
|
||||||
|
|
||||||
|
|
||||||
def inputMainMenu():
|
# only for testing purposes
|
||||||
consoleTitle()
|
|
||||||
clearConsole()
|
|
||||||
checkConfig()
|
|
||||||
check_requirements()
|
|
||||||
createInputLists()
|
|
||||||
printMainMenu()
|
|
||||||
getInput()
|
|
||||||
outputTest()
|
|
||||||
|
|
||||||
|
|
||||||
def outputTest():
|
def outputTest():
|
||||||
print("Hello world")
|
print("Hello world")
|
||||||
print("Waiting still seconds: 5", end='\r')
|
print("Waiting still seconds: 5", end='\r')
|
||||||
@ -97,6 +88,3 @@ def outputTest():
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
print("Done ✅☑✔ ")
|
print("Done ✅☑✔ ")
|
||||||
input("Press key to end program...")
|
input("Press key to end program...")
|
||||||
|
|
||||||
|
|
||||||
inputMainMenu()
|
|
59
src/handlers/handle_sftp.py
Normal file
59
src/handlers/handle_sftp.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import sys
|
||||||
|
import pysftp
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
from utils.consoleoutput import oColors
|
||||||
|
from handlers.handle_config import checkConfig
|
||||||
|
|
||||||
|
|
||||||
|
def createSFTPConnection():
|
||||||
|
cnopts = pysftp.CnOpts()
|
||||||
|
cnopts.hostkeys = None # TODO fix this
|
||||||
|
try:
|
||||||
|
sftp = pysftp.Connection(checkConfig().sftp_server, username=checkConfig().sftp_user, \
|
||||||
|
password=checkConfig().sftp_password, port=checkConfig().sftp_port, cnopts=cnopts)
|
||||||
|
except paramiko.ssh_exception.AuthenticationException:
|
||||||
|
print(oColors.brightRed + "[SFTP]: Wrong Username/Password" + oColors.standardWhite)
|
||||||
|
except paramiko.ssh_exception.SSHException:
|
||||||
|
print(oColors.brightRed + "[SFTP]: The SFTP server isn't available." + oColors.standardWhite)
|
||||||
|
try:
|
||||||
|
return sftp
|
||||||
|
except UnboundLocalError:
|
||||||
|
print(oColors.brightRed + "[SFTP]: Check your config.ini!" + oColors.standardWhite)
|
||||||
|
print(oColors.brightRed + "Exiting program..." + oColors.standardWhite)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def sftp_showPlugins(sftp):
|
||||||
|
sftp.cd('plugins')
|
||||||
|
for attr in sftp.listdir_attr():
|
||||||
|
print(attr.filename, attr)
|
||||||
|
|
||||||
|
|
||||||
|
def sftp_cdPluginDir(sftp):
|
||||||
|
sftp.cd('plugins')
|
||||||
|
|
||||||
|
|
||||||
|
def sftp_upload_file(sftp, itemPath):
|
||||||
|
try:
|
||||||
|
sftp.chdir('plugins')
|
||||||
|
sftp.put(itemPath)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(oColors.brightRed + "The *plugins* folder couldn*t be found on the remote host!" + oColors.standardWhite)
|
||||||
|
print(oColors.brightRed + "Aborting installation." + oColors.standardWhite)
|
||||||
|
sftp.close()
|
||||||
|
|
||||||
|
|
||||||
|
def sftp_listAll(sftp):
|
||||||
|
try:
|
||||||
|
sftp.chdir('plugins')
|
||||||
|
installedPlugins = sftp.listdir()
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(oColors.brightRed + "The *plugins* folder couldn*t be found on the remote host!" + oColors.standardWhite)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return installedPlugins
|
||||||
|
except UnboundLocalError:
|
||||||
|
print(oColors.brightRed + "No plugins were found." + oColors.standardWhite)
|
0
src/plugin/__init__.py
Normal file
0
src/plugin/__init__.py
Normal file
@ -1,11 +1,12 @@
|
|||||||
|
import re
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from urllib.error import HTTPError
|
from urllib.error import HTTPError
|
||||||
import re
|
|
||||||
from web_request import doAPIRequest
|
from utils.consoleoutput import oColors
|
||||||
from consoleoutput import oColors
|
from utils.web_request import doAPIRequest
|
||||||
from handle_config import checkConfig
|
from utils.utilities import createTempPluginFolder, deleteTempPluginFolder
|
||||||
from utilities import createTempPluginFolder, deleteTempPluginFolder
|
from handlers.handle_config import checkConfig
|
||||||
from handle_sftp import sftp_upload_file, sftp_cdPluginDir, createSFTPConnection
|
from handlers.handle_sftp import sftp_upload_file, sftp_cdPluginDir, createSFTPConnection
|
||||||
|
|
||||||
|
|
||||||
def calculateFileSize(downloadFileSize):
|
def calculateFileSize(downloadFileSize):
|
||||||
@ -102,7 +103,6 @@ def downloadSpecificVersion(ressourceId, downloadPath, versionID='latest'):
|
|||||||
print(f"Downloadsize: {filesizeData} KB")
|
print(f"Downloadsize: {filesizeData} KB")
|
||||||
print(f"File downloaded here: {downloadPath}")
|
print(f"File downloaded here: {downloadPath}")
|
||||||
if not checkConfig().localPluginFolder:
|
if not checkConfig().localPluginFolder:
|
||||||
print(downloadPath)
|
|
||||||
sftpSession = createSFTPConnection()
|
sftpSession = createSFTPConnection()
|
||||||
sftp_upload_file(sftpSession, downloadPath)
|
sftp_upload_file(sftpSession, downloadPath)
|
||||||
|
|
48
src/plugin/plugin_remover.py
Normal file
48
src/plugin/plugin_remover.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from utils.consoleoutput import oColors
|
||||||
|
from handlers.handle_config import checkConfig
|
||||||
|
from handlers.handle_sftp import createSFTPConnection, sftp_listAll
|
||||||
|
from plugin.plugin_updatechecker import getFileName, getFileVersion, getInstalledPlugin, createPluginList
|
||||||
|
|
||||||
|
|
||||||
|
def removePlugin(pluginToRemove):
|
||||||
|
createPluginList()
|
||||||
|
if not checkConfig().localPluginFolder:
|
||||||
|
sftp = createSFTPConnection()
|
||||||
|
pluginList = sftp_listAll(sftp)
|
||||||
|
else:
|
||||||
|
pluginList = os.listdir(checkConfig().pathToPluginFolder)
|
||||||
|
i = 0
|
||||||
|
try:
|
||||||
|
for plugin in pluginList:
|
||||||
|
try:
|
||||||
|
fileName = getFileName(plugin)
|
||||||
|
fileVersion = getFileVersion(plugin)
|
||||||
|
pluginId = getInstalledPlugin(fileName, fileVersion)
|
||||||
|
except TypeError:
|
||||||
|
continue
|
||||||
|
pluginIdStr = str(pluginId)
|
||||||
|
|
||||||
|
if pluginToRemove == pluginIdStr or re.search(pluginToRemove, fileName, re.IGNORECASE):
|
||||||
|
print(f"Removing: {fileName}")
|
||||||
|
if not checkConfig().localPluginFolder:
|
||||||
|
pluginPath = checkConfig().sftp_folderPath
|
||||||
|
pluginPath = f"{pluginPath}\\{plugin}"
|
||||||
|
sftp = createSFTPConnection()
|
||||||
|
sftp.remove(pluginPath)
|
||||||
|
print(f"Removed: {fileName}")
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
pluginPath = checkConfig().pathToPluginFolder
|
||||||
|
pluginPath = f"{pluginPath}\\{plugin}"
|
||||||
|
os.remove(pluginPath)
|
||||||
|
print(f"Removed: {fileName}")
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
except TypeError:
|
||||||
|
print(oColors.brightRed + f"Aborted removing of: {pluginToRemove}." + oColors.standardWhite)
|
||||||
|
if i == 0:
|
||||||
|
print(oColors.brightRed + f"Couldn't remove plugin: {pluginToRemove}" + oColors.standardWhite)
|
@ -1,11 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from consoleoutput import oColors
|
from utils.consoleoutput import oColors
|
||||||
from plugin_downloader import getSpecificPackage #handleInput
|
from utils.web_request import doAPIRequest
|
||||||
from web_request import doAPIRequest
|
from handlers.handle_config import checkConfig
|
||||||
from handle_config import checkConfig
|
from handlers.handle_sftp import createSFTPConnection, sftp_listAll
|
||||||
from handle_sftp import createSFTPConnection, sftp_listAll
|
from plugin.plugin_downloader import getSpecificPackage
|
||||||
|
|
||||||
|
|
||||||
def createPluginList():
|
def createPluginList():
|
||||||
@ -31,7 +31,6 @@ def getFileName(pluginName):
|
|||||||
|
|
||||||
|
|
||||||
def getFileVersion(pluginName):
|
def getFileVersion(pluginName):
|
||||||
#pluginVersionString = None
|
|
||||||
pluginNameFull = pluginName
|
pluginNameFull = pluginName
|
||||||
pluginVersion = re.search(r'([\d.]+[.jar]+)', pluginNameFull)
|
pluginVersion = re.search(r'([\d.]+[.jar]+)', pluginNameFull)
|
||||||
pluginVersionFull = pluginVersion.group()
|
pluginVersionFull = pluginVersion.group()
|
||||||
@ -60,7 +59,7 @@ def checkInstalledPackage(inputSelectedObject="all"):
|
|||||||
i = 0
|
i = 0
|
||||||
oldPackages = 0
|
oldPackages = 0
|
||||||
print("Index / Name / Installed Version / Update available")
|
print("Index / Name / Installed Version / Update available")
|
||||||
|
try:
|
||||||
for plugin in pluginList:
|
for plugin in pluginList:
|
||||||
try:
|
try:
|
||||||
fileName = getFileName(plugin)
|
fileName = getFileName(plugin)
|
||||||
@ -92,6 +91,8 @@ def checkInstalledPackage(inputSelectedObject="all"):
|
|||||||
print(f"[{i+1}] {fileName} - {fileVersion} - {pluginIsOutdated}")
|
print(f"[{i+1}] {fileName} - {fileVersion} - {pluginIsOutdated}")
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
except TypeError:
|
||||||
|
print(oColors.brightRed + "Aborted checking for plugins." + oColors.standardWhite)
|
||||||
print(f"Old packages: [{oldPackages}/{i}]")
|
print(f"Old packages: [{oldPackages}/{i}]")
|
||||||
|
|
||||||
|
|
||||||
@ -104,6 +105,7 @@ def updateInstalledPackage(inputSelectedObject='all'):
|
|||||||
pluginList = os.listdir(checkConfig().pathToPluginFolder)
|
pluginList = os.listdir(checkConfig().pathToPluginFolder)
|
||||||
i = 0
|
i = 0
|
||||||
pluginsUpdated = 0
|
pluginsUpdated = 0
|
||||||
|
try:
|
||||||
for plugin in pluginList:
|
for plugin in pluginList:
|
||||||
print(plugin)
|
print(plugin)
|
||||||
try:
|
try:
|
||||||
@ -156,6 +158,8 @@ def updateInstalledPackage(inputSelectedObject='all'):
|
|||||||
getSpecificPackage(pluginId, checkConfig().pathToPluginFolder)
|
getSpecificPackage(pluginId, checkConfig().pathToPluginFolder)
|
||||||
pluginsUpdated += 1
|
pluginsUpdated += 1
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
except TypeError:
|
||||||
|
print(oColors.brightRed + "Aborted updating for plugins." + oColors.standardWhite)
|
||||||
print(f"[{pluginsUpdated}/{i}] Plugins updated")
|
print(f"[{pluginsUpdated}/{i}] Plugins updated")
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
from handle_config import checkConfig
|
|
||||||
from handle_sftp import createSFTPConnection, sftp_listAll
|
|
||||||
|
|
||||||
def removePlugin(pluginToRemove):
|
|
||||||
if not checkConfig().localPluginFolder:
|
|
||||||
sftp = createSFTPConnection()
|
|
||||||
pluginList = sftp_listAll(sftp)
|
|
||||||
else:
|
|
||||||
pluginList = os.listdir(checkConfig().pathToPluginFolder)
|
|
||||||
|
|
||||||
for plugin in pluginList:
|
|
||||||
#pluginVersion = re.search(pluginToRemove, pluginNameFull)
|
|
||||||
print(plugin)
|
|
0
src/utils/__init__.py
Normal file
0
src/utils/__init__.py
Normal file
@ -1,10 +1,11 @@
|
|||||||
# misc functions
|
# misc functions
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import requests
|
|
||||||
import shutil
|
import shutil
|
||||||
from consoleoutput import oColors
|
import requests
|
||||||
from handle_config import checkConfig
|
|
||||||
|
from utils.consoleoutput import oColors
|
||||||
|
from handlers.handle_config import checkConfig
|
||||||
|
|
||||||
|
|
||||||
def getHelp():
|
def getHelp():
|
||||||
@ -34,7 +35,7 @@ def apiTest():
|
|||||||
try:
|
try:
|
||||||
r = requests.get(apiStatusUrl)
|
r = requests.get(apiStatusUrl)
|
||||||
except requests.exceptions.HTTPError:
|
except requests.exceptions.HTTPError:
|
||||||
print(oColors.brightRed + "Couldn*t make a connection to the API. Check you connection to the internet!" + oColors.standardWhite)
|
print(oColors.brightRed + "Couldn't make a connection to the API. Check you connection to the internet!" + oColors.standardWhite)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
print(oColors.brightRed + "Problems with the API detected. Plese try it again later!" + oColors.standardWhite)
|
print(oColors.brightRed + "Problems with the API detected. Plese try it again later!" + oColors.standardWhite)
|
@ -1,6 +1,5 @@
|
|||||||
# Handles the web requests
|
# Handles the web requests
|
||||||
import requests
|
import requests
|
||||||
#import urllib.request
|
|
||||||
|
|
||||||
|
|
||||||
def doAPIRequest(url):
|
def doAPIRequest(url):
|
Loading…
Reference in New Issue
Block a user