modulitiz-micro 2.26.0__py311-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modulitiz_micro/ModuloBase64.py +61 -0
- modulitiz_micro/ModuloColorText.py +35 -0
- modulitiz_micro/ModuloDate.py +295 -0
- modulitiz_micro/ModuloFunzioni.py +58 -0
- modulitiz_micro/ModuloListe.py +150 -0
- modulitiz_micro/ModuloMeteo.py +72 -0
- modulitiz_micro/ModuloNumeri.py +130 -0
- modulitiz_micro/ModuloPyinstaller.py +29 -0
- modulitiz_micro/ModuloSeriale.py +61 -0
- modulitiz_micro/ModuloStatistiche.py +31 -0
- modulitiz_micro/ModuloStringhe.py +180 -0
- modulitiz_micro/ModuloTarghe.py +46 -0
- modulitiz_micro/android/ModuloAndroid.py +18 -0
- modulitiz_micro/android/ModuloAndroidAdb.py +48 -0
- modulitiz_micro/android/ModuloAndroidSim.py +130 -0
- modulitiz_micro/android/beans/SmsBean.py +12 -0
- modulitiz_micro/android/enums/AndroidSmsTypeEnum.py +17 -0
- modulitiz_micro/database/AbstractDatabaseService.py +13 -0
- modulitiz_micro/database/AbstractSql.py +49 -0
- modulitiz_micro/database/ModuloSqlOracle.py +19 -0
- modulitiz_micro/database/ModuloSqlServer.py +50 -0
- modulitiz_micro/database/eccezioni/EccezioneDbNoData.py +6 -0
- modulitiz_micro/database/mysql/AbstractBasicMysql.py +114 -0
- modulitiz_micro/database/mysql/ModuloMysql.py +151 -0
- modulitiz_micro/database/mysql/MysqlCommonConverter.py +47 -0
- modulitiz_micro/database/mysql/eccezioni/EccezioneMysqlOffline.py +6 -0
- modulitiz_micro/database/sqlite/AbstractBasicSQLite.py +114 -0
- modulitiz_micro/database/sqlite/ModuloSQLite.py +82 -0
- modulitiz_micro/eccezioni/EccezioneBase.py +7 -0
- modulitiz_micro/eccezioni/EccezioneCtrlC.py +7 -0
- modulitiz_micro/eccezioni/EccezioneRuntime.py +7 -0
- modulitiz_micro/eccezioni/EccezioneScheduler.py +7 -0
- modulitiz_micro/eccezioni/EccezioneSoNonSupportato.py +7 -0
- modulitiz_micro/eccezioni/http/EccezioneHttp.py +8 -0
- modulitiz_micro/eccezioni/http/EccezioneHttp404.py +7 -0
- modulitiz_micro/eccezioni/http/EccezioneHttpGeneric.py +7 -0
- modulitiz_micro/files/ModuloFiles.py +173 -0
- modulitiz_micro/files/ModuloLogging.py +69 -0
- modulitiz_micro/files/ModuloZip.py +42 -0
- modulitiz_micro/files/cache/CacheBean.py +5 -0
- modulitiz_micro/files/cache/CacheRam.py +29 -0
- modulitiz_micro/files/cache/DatabaseCache.py +91 -0
- modulitiz_micro/files/cache/decorators/cacheRam.py +26 -0
- modulitiz_micro/files/git/ModuloGit.py +15 -0
- modulitiz_micro/gestionedom/GestioneDom.py +44 -0
- modulitiz_micro/init/AbstractBasicInit.py +27 -0
- modulitiz_micro/init/AbstractInit.py +11 -0
- modulitiz_micro/keylogger/EccezioneKeyLogger.py +7 -0
- modulitiz_micro/keylogger/ModuloKeylogger.py +73 -0
- modulitiz_micro/multithreading/ModuloThread.py +26 -0
- modulitiz_micro/multithreading/ModuloThreadLogger.py +8 -0
- modulitiz_micro/multithreading/ModuloThreadWithCallbackError.py +25 -0
- modulitiz_micro/nlp/ModuloNlp.py +36 -0
- modulitiz_micro/nlp/ModuloNlpDateAndTime.py +59 -0
- modulitiz_micro/rete/ModuloEmail.py +69 -0
- modulitiz_micro/rete/ModuloNetworking.py +64 -0
- modulitiz_micro/rete/ModuloOpenVpn.py +15 -0
- modulitiz_micro/rete/http/ModuloHttp.py +114 -0
- modulitiz_micro/rete/http/ModuloHttpConnectionSafe.py +91 -0
- modulitiz_micro/rete/http/ModuloHttpUtils.py +66 -0
- modulitiz_micro/rete/http/beans/HttpResponseBean.py +5 -0
- modulitiz_micro/rete/http/decorators/catchAndRaiseHttpExceptions.py +22 -0
- modulitiz_micro/rete/ntp/AbstractModuloNtp.py +73 -0
- modulitiz_micro/rete/ntp/ModuloNtpIt.py +8 -0
- modulitiz_micro/rete/socketserver/AbstractBasicGetSocketServer.py +35 -0
- modulitiz_micro/rete/socketserver/AbstractSocketServer.py +267 -0
- modulitiz_micro/rete/ssl/ModuloSsl.py +56 -0
- modulitiz_micro/sistema/EnvVarsEnum.py +9 -0
- modulitiz_micro/sistema/ModuloEnvVars.py +34 -0
- modulitiz_micro/sistema/ModuloSystem.py +298 -0
- modulitiz_micro/sistema/ModuloSystemPipe.py +67 -0
- modulitiz_micro/social/telegram/ModuloTelegram.py +52 -0
- modulitiz_micro/social/telegram/ModuloTelegramSimple.py +26 -0
- modulitiz_micro/util/beans/conf/AbstractBasicConfBean.py +11 -0
- modulitiz_micro/util/beans/conf/AbstractConfBean.py +16 -0
- modulitiz_micro/util/beans/fileconf/AbstractBasicFileConfBean.py +11 -0
- modulitiz_micro/util/beans/fileconf/AbstractFileConfBean.py +13 -0
- modulitiz_micro/util/beans/globalvar/AbstractBasicGlobalVarBean.py +15 -0
- modulitiz_micro/util/beans/globalvar/AbstractGlobalVarBean.py +34 -0
- modulitiz_micro/util/decorators/noAwait.py +23 -0
- modulitiz_micro/util/pip/AbstractModuloPip.py +41 -0
- modulitiz_micro/util/pip/ModuloPip.py +49 -0
- modulitiz_micro/util/scheduler/AbstractScheduler.py +32 -0
- modulitiz_micro/util/spooler/AbstractSpooler.py +14 -0
- modulitiz_micro/util/spooler/Spooler.py +18 -0
- modulitiz_micro/util/spooler/beans/QueueBean.py +8 -0
- modulitiz_micro/util/spooler/decorators/spooler.py +49 -0
- modulitiz_micro/util/spooler/eccezioni/EccezioneSpooler.py +7 -0
- modulitiz_micro/util/spooler/eccezioni/EccezioneSpoolerFull.py +7 -0
- modulitiz_micro/util/unittesting/AbstractOverrideTestUtil.py +18 -0
- modulitiz_micro/util/unittesting/AbstractTestUtil.py +11 -0
- modulitiz_micro/util/unittesting/ModuloRunUnitTest.py +25 -0
- modulitiz_micro/util/wheel/ModuloBuildWheel.py +118 -0
- modulitiz_micro/util/wheel/ModuloToml.py +40 -0
- modulitiz_micro/util/wheel/ModuloWheel.py +12 -0
- modulitiz_micro-2.26.0.dist-info/LICENSE +21 -0
- modulitiz_micro-2.26.0.dist-info/METADATA +63 -0
- modulitiz_micro-2.26.0.dist-info/RECORD +100 -0
- modulitiz_micro-2.26.0.dist-info/WHEEL +5 -0
- modulitiz_micro-2.26.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
import threading
|
2
|
+
|
3
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
4
|
+
from modulitiz_micro.multithreading.ModuloThreadLogger import ModuloThreadLogger
|
5
|
+
from modulitiz_micro.multithreading.ModuloThreadWithCallbackError import ModuloThreadWithCallbackError
|
6
|
+
|
7
|
+
|
8
|
+
class ModuloThread(object):
|
9
|
+
@staticmethod
|
10
|
+
def startDaemon(logger:ModuloLogging,funzione,args:tuple)->ModuloThreadLogger:
|
11
|
+
d=ModuloThreadLogger(logger,name='th__'+funzione.__name__,target=funzione,args=args,daemon=True)
|
12
|
+
d.start()
|
13
|
+
return d
|
14
|
+
@staticmethod
|
15
|
+
def startDaemonWithCallbackError(callbackError,funzione,args:tuple)->ModuloThreadWithCallbackError:
|
16
|
+
d=ModuloThreadWithCallbackError(callbackError,name='th__'+funzione.__name__,target=funzione,args=args,daemon=True)
|
17
|
+
d.start()
|
18
|
+
return d
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def numeroThreadsAttivi()->int:
|
22
|
+
"""
|
23
|
+
Non e' incluso il main, ovviamente.
|
24
|
+
Se usi pydev debugger devi sottrarre altri 2 o 3 threads
|
25
|
+
"""
|
26
|
+
return threading.active_count()-1
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
2
|
+
from modulitiz_micro.multithreading.ModuloThreadWithCallbackError import ModuloThreadWithCallbackError
|
3
|
+
|
4
|
+
|
5
|
+
class ModuloThreadLogger(ModuloThreadWithCallbackError):
|
6
|
+
|
7
|
+
def __init__(self,logger:ModuloLogging,**kwargs):
|
8
|
+
super().__init__(lambda ex:logger.exception('Eccezione nel thread:'),**kwargs)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import threading
|
2
|
+
|
3
|
+
|
4
|
+
class ModuloThreadWithCallbackError(threading.Thread):
|
5
|
+
"""
|
6
|
+
This class should always be used in preference to threading.Thread.
|
7
|
+
|
8
|
+
The interface provided by this class is identical to that of threading.Thread,
|
9
|
+
however, if an exception occurs in the thread the callback function is called,
|
10
|
+
rather than printed to stderr.
|
11
|
+
|
12
|
+
This is important in daemon style applications where stderr is redirected to /dev/null.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self,callbackError,**kwargs):
|
16
|
+
super().__init__(**kwargs)
|
17
|
+
self.callbackError=callbackError
|
18
|
+
self._realRun=self.run
|
19
|
+
self.run=self.__wrapRun
|
20
|
+
|
21
|
+
def __wrapRun(self):
|
22
|
+
try:
|
23
|
+
self._realRun()
|
24
|
+
except Exception as ex:
|
25
|
+
self.callbackError(ex)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class ModuloNlp(object):
|
2
|
+
|
3
|
+
@staticmethod
|
4
|
+
def giornoRelativoToParola(giornoRel:int)->str|None:
|
5
|
+
match giornoRel:
|
6
|
+
case -1: return "ieri"
|
7
|
+
case 0: return "oggi"
|
8
|
+
case 1: return "domani"
|
9
|
+
case 2: return "dopodomani"
|
10
|
+
case _: return None
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def giornoRelativoToNum(giornoRel:str)->int|None:
|
14
|
+
match giornoRel:
|
15
|
+
case "ieri": return -1
|
16
|
+
case "oggi": return 0
|
17
|
+
case "domani": return 1
|
18
|
+
case "dopodomani": return 2
|
19
|
+
case _: return None
|
20
|
+
|
21
|
+
@staticmethod
|
22
|
+
def verboEssere(isPassato:bool)->str:
|
23
|
+
if isPassato:
|
24
|
+
return "era"
|
25
|
+
return "è"
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def articolo(isDeterminativo:bool,isMaschile:bool)->str:
|
29
|
+
if isDeterminativo:
|
30
|
+
if isMaschile:
|
31
|
+
return "il"
|
32
|
+
return "la"
|
33
|
+
if isMaschile:
|
34
|
+
return "un"
|
35
|
+
return "una"
|
36
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from modulitiz_micro.ModuloDate import ModuloDate
|
2
|
+
from modulitiz_micro.ModuloNumeri import ModuloNumeri
|
3
|
+
from modulitiz_micro.nlp.ModuloNlp import ModuloNlp
|
4
|
+
|
5
|
+
|
6
|
+
class ModuloNlpDateAndTime(object):
|
7
|
+
@staticmethod
|
8
|
+
def dateToString(data,anno:str)->str:
|
9
|
+
dataStr=ModuloDate.dateToString(data,"%w %A %d %B")
|
10
|
+
numGiornoSett,giornoSett,giorno,mese=dataStr.split(" ")
|
11
|
+
# sistemo grammaticalmente gli articoli, verbi, ...
|
12
|
+
numGiornoSett=int(numGiornoSett)
|
13
|
+
giorno=int(giorno)
|
14
|
+
articoloGiornoMese="il "
|
15
|
+
giornoParola=giorno
|
16
|
+
if giorno in (1,8,11):
|
17
|
+
articoloGiornoMese="l'"
|
18
|
+
giornoParola=ModuloNumeri.numberToWord(giorno)
|
19
|
+
verbo=ModuloNlp.verboEssere(data<ModuloDate.now())
|
20
|
+
articoloGiornoSett=ModuloNlp.articolo(False,numGiornoSett!=0)
|
21
|
+
output=f"{articoloGiornoMese}{giornoParola} {mese}{anno} {verbo} {articoloGiornoSett} {giornoSett}"
|
22
|
+
output=output.lower()
|
23
|
+
return output
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def minutesToTimeRelative(cls,minutes:int,arrotonda:bool)->str:
|
27
|
+
prefixTempo=""
|
28
|
+
minutes,hoursDiff,hoursWord=cls.__minutesToHours(minutes)
|
29
|
+
# minuti
|
30
|
+
minutesWord=minutes
|
31
|
+
if minutes==1:
|
32
|
+
minutesWord="un"
|
33
|
+
elif 10<minutes<60:
|
34
|
+
if arrotonda is True and minutes%10!=0:
|
35
|
+
prefixTempo=" circa"
|
36
|
+
minutes=round(minutes,-1)
|
37
|
+
minutesWord=minutes
|
38
|
+
output="Fino a"+prefixTempo
|
39
|
+
if hoursDiff>0:
|
40
|
+
output+=" %s or%s"%(hoursWord,("a" if hoursDiff==1 else "e"))
|
41
|
+
if minutes>0:
|
42
|
+
output+=" e"
|
43
|
+
if minutes>0:
|
44
|
+
output+=" %s minut%s"%(minutesWord,("o" if minutes==1 else "i"))
|
45
|
+
return output
|
46
|
+
|
47
|
+
@staticmethod
|
48
|
+
def __minutesToHours(minutes:int)->tuple:
|
49
|
+
hoursDiff=0
|
50
|
+
hoursWord=""
|
51
|
+
if minutes<60:
|
52
|
+
return minutes,hoursDiff,hoursWord
|
53
|
+
hoursDiff=minutes//60
|
54
|
+
minutes=minutes%60
|
55
|
+
if hoursDiff==1:
|
56
|
+
hoursWord="un"
|
57
|
+
else:
|
58
|
+
hoursWord=hoursDiff
|
59
|
+
return minutes,hoursDiff,hoursWord
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import smtplib
|
2
|
+
|
3
|
+
from modulitiz_micro.ModuloDate import ModuloDate
|
4
|
+
from modulitiz_micro.ModuloListe import ModuloListe
|
5
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
6
|
+
|
7
|
+
|
8
|
+
class ModuloEmail(object):
|
9
|
+
SERVER_GOOGLE_INVIO=('smtp.gmail.com',smtplib.SMTP_SSL_PORT)
|
10
|
+
SERVER_VIRGILIO_INVIO=('out.virgilio.it',smtplib.SMTP_SSL_PORT)
|
11
|
+
|
12
|
+
def __init__(self,credenzialiServer:tuple,isDebug:bool=False):
|
13
|
+
self.credenzialiServer=credenzialiServer
|
14
|
+
self.isDebug=isDebug
|
15
|
+
self.connEmail=None
|
16
|
+
self.utente=None
|
17
|
+
self.isLogged=False
|
18
|
+
|
19
|
+
def login(self,utente:str|None=None,password:str|None=None):
|
20
|
+
if self.isLogged:
|
21
|
+
return
|
22
|
+
self.connEmail=smtplib.SMTP_SSL(*self.credenzialiServer)
|
23
|
+
if self.isDebug:
|
24
|
+
self.connEmail.set_debuglevel(1)
|
25
|
+
# se serve setto l'autenticazione
|
26
|
+
if utente is not None and password is not None:
|
27
|
+
self.utente=utente
|
28
|
+
self.connEmail.login(utente,password)
|
29
|
+
self.isLogged=True
|
30
|
+
|
31
|
+
def inviaEmail(self, mittente:str|None,destinatari, oggetto:str, messaggio:str,
|
32
|
+
isHtml:bool, dataInvio=ModuloDate.now(), cc=None, ccn=None)->dict:
|
33
|
+
# controllo i parametri
|
34
|
+
dataInvio=ModuloDate.dateToString(dataInvio)
|
35
|
+
if isinstance(destinatari, str):
|
36
|
+
destinatari=[destinatari]
|
37
|
+
if mittente is None:
|
38
|
+
mittente=self.utente
|
39
|
+
domain=self.utente.split("@")[-1]
|
40
|
+
messageId=f"{ModuloDate.getSecs()}@{domain}"
|
41
|
+
# creo il messaggio
|
42
|
+
message=f"""Date: {dataInvio}
|
43
|
+
From: {mittente}
|
44
|
+
Subject: {oggetto}
|
45
|
+
To: {", ".join(destinatari)}
|
46
|
+
Message-ID: <{messageId}>
|
47
|
+
"""
|
48
|
+
if not ModuloListe.isEmpty(cc):
|
49
|
+
message+=("Cc: "+", ".join(cc))+"\n"
|
50
|
+
if not ModuloListe.isEmpty(ccn):
|
51
|
+
message+=("Bcc: "+", ".join(ccn))+"\n"
|
52
|
+
message+="Content-Type: text/html;\n"
|
53
|
+
# converto il messaggio in formato html
|
54
|
+
if not isHtml:
|
55
|
+
messaggio=ModuloStringhe.normalizzaEol(messaggio).replace("\n","<br/>\n")
|
56
|
+
messaggio=messaggio.encode(ModuloStringhe.CODIFICA_ASCII,"xmlcharrefreplace").decode(ModuloStringhe.CODIFICA_UTF8)
|
57
|
+
message+="\n"+messaggio
|
58
|
+
# invio la mail
|
59
|
+
try:
|
60
|
+
return self.connEmail.sendmail(mittente,destinatari,message)
|
61
|
+
except smtplib.SMTPServerDisconnected as ex:
|
62
|
+
return {"":str(ex)}
|
63
|
+
|
64
|
+
def close(self):
|
65
|
+
if self.connEmail is None:
|
66
|
+
return
|
67
|
+
self.connEmail.quit()
|
68
|
+
self.connEmail=None
|
69
|
+
self.isLogged=False
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import os
|
2
|
+
import socket
|
3
|
+
from contextlib import closing
|
4
|
+
from uuid import getnode
|
5
|
+
|
6
|
+
from _socket import gaierror
|
7
|
+
|
8
|
+
from modulitiz_micro.ModuloListe import ModuloListe
|
9
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
10
|
+
from modulitiz_micro.eccezioni.EccezioneRuntime import EccezioneRuntime
|
11
|
+
from modulitiz_micro.sistema.ModuloSystem import ModuloSystem
|
12
|
+
|
13
|
+
|
14
|
+
class ModuloNetworking(object):
|
15
|
+
@staticmethod
|
16
|
+
def getMacAddress()->str:
|
17
|
+
mac=("%012X" % getnode())
|
18
|
+
return mac
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def getIp()->str|None:
|
22
|
+
sockObj = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
23
|
+
sockObj.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
24
|
+
# doesn't even have to be reachable
|
25
|
+
try:
|
26
|
+
sockObj.connect(('255.255.255.255', 1))
|
27
|
+
indirizzoIp = sockObj.getsockname()[0]
|
28
|
+
except gaierror:
|
29
|
+
indirizzoIp = None
|
30
|
+
finally:
|
31
|
+
sockObj.close()
|
32
|
+
return indirizzoIp
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def isHttpPortOpen(host:str|None,port:int)->bool:
|
36
|
+
# controllo host
|
37
|
+
if host is None:
|
38
|
+
host="127.0.0.1"
|
39
|
+
# controllo porta
|
40
|
+
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
|
41
|
+
if sock.connect_ex((host, port)) == 0:
|
42
|
+
return True
|
43
|
+
return False
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def checkPing(indirizzoIp:str)->bool:
|
47
|
+
# costruisco il comando a seconda del sistema operativo
|
48
|
+
comando="ping "
|
49
|
+
if ModuloSystem.isWindows():
|
50
|
+
comando+="-n"
|
51
|
+
elif os.name=='posix':
|
52
|
+
comando+="-c"
|
53
|
+
else:
|
54
|
+
raise EccezioneRuntime("Tipologia del sistema operativo non riconosciuta: "+os.name)
|
55
|
+
comando+=" 1 " + indirizzoIp
|
56
|
+
# eseguo il comando
|
57
|
+
outputComando=ModuloStringhe.normalizzaEol(ModuloSystem.systemCallReturnOutput(comando,None))
|
58
|
+
righe=outputComando.split("\n")
|
59
|
+
righe=ModuloListe.eliminaElementiVuoti(righe)
|
60
|
+
for riga in righe:
|
61
|
+
if ModuloStringhe.contains(riga, "%"):
|
62
|
+
numPacchettiPersi=int(riga.split("=")[1].split("(")[0].strip())
|
63
|
+
return numPacchettiPersi==0
|
64
|
+
return False
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from modulitiz_micro.sistema.ModuloSystem import ModuloSystem
|
2
|
+
|
3
|
+
|
4
|
+
class ModuloOpenVpn(object):
|
5
|
+
def __init__(self,percorsoOpenVpn:str):
|
6
|
+
self.__percorsoOpenVpn=percorsoOpenVpn
|
7
|
+
|
8
|
+
def startVpn(self,isOpenVpnAlreadyRunning:bool,filenameOvpn:str):
|
9
|
+
"""
|
10
|
+
Send a command to start vpn to a new or already running instance of the GUI.
|
11
|
+
https://community.openvpn.net/openvpn/wiki/OpenVPN-GUI-New#gui-help
|
12
|
+
"""
|
13
|
+
subCmd="--command connect" if isOpenVpnAlreadyRunning else "--connect"
|
14
|
+
cmd=r'start "" "{}" {} {}.ovpn'.format(self.__percorsoOpenVpn,subCmd,filenameOvpn)
|
15
|
+
ModuloSystem.systemCallWaitAndClose(cmd,False)
|
@@ -0,0 +1,114 @@
|
|
1
|
+
import gzip
|
2
|
+
import logging
|
3
|
+
import ssl
|
4
|
+
import urllib
|
5
|
+
from urllib.parse import urlencode
|
6
|
+
from urllib.request import Request
|
7
|
+
|
8
|
+
import brotli
|
9
|
+
|
10
|
+
from modulitiz_micro.ModuloBase64 import ModuloBase64
|
11
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
12
|
+
from modulitiz_micro.eccezioni.EccezioneRuntime import EccezioneRuntime
|
13
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
14
|
+
from modulitiz_micro.rete.http.ModuloHttpConnectionSafe import ModuloHttpConnectionSafe
|
15
|
+
from modulitiz_micro.rete.http.beans.HttpResponseBean import HttpResponseBean
|
16
|
+
from modulitiz_micro.rete.http.decorators.catchAndRaiseHttpExceptions import catchAndRaiseHttpExceptions
|
17
|
+
|
18
|
+
|
19
|
+
class ModuloHttp(object):
|
20
|
+
"""
|
21
|
+
Utility per gestione richieste di rete secondo il protocollo HTTP.
|
22
|
+
"""
|
23
|
+
|
24
|
+
UA_ANDROID="Mozilla/5.0 (Linux; Android 12.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Mobile Safari/537.36"
|
25
|
+
|
26
|
+
UA_MACOS_CHROME="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
|
27
|
+
UA_WINDOWS_CHROME="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
|
28
|
+
UA_WINDOWS_FIREFOX="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0"
|
29
|
+
|
30
|
+
UAS=[UA_MACOS_CHROME,UA_WINDOWS_CHROME,UA_WINDOWS_FIREFOX]
|
31
|
+
|
32
|
+
URL_CERCA_GOOGLE="https://www.google.it/search?q="
|
33
|
+
URLS_GET_IP=('https://ipecho.net/plain','https://ipinfo.io/ip','https://api.ipify.org')
|
34
|
+
|
35
|
+
REGEX_INDIRIZZO_IP=r"\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b"
|
36
|
+
|
37
|
+
STATUS_OK=200
|
38
|
+
STATUS_OK_PARTIAL_CONTENT=206
|
39
|
+
|
40
|
+
|
41
|
+
def __init__(self,url:str,logger:ModuloLogging|None,useProxy:bool=False):
|
42
|
+
logging.getLogger("urllib3").propagate=False
|
43
|
+
|
44
|
+
self.__requestObj=Request(url)
|
45
|
+
self.__logger=logger
|
46
|
+
|
47
|
+
self.setUserAgent(self.UA_WINDOWS_FIREFOX)
|
48
|
+
self.addHeader('Accept-Encoding','gzip, deflate, br') # questo serve per ridurre la dimensione della risposta
|
49
|
+
if useProxy is True:
|
50
|
+
self.addHeader('Origin','localhost')
|
51
|
+
self.connectionSafe=ModuloHttpConnectionSafe(self.__logger)
|
52
|
+
self.postData=None
|
53
|
+
|
54
|
+
def doGet(self,retries:int,ignoreCerts:bool=False)->HttpResponseBean:
|
55
|
+
"""
|
56
|
+
Esegue una richiesta di tipo GET
|
57
|
+
"""
|
58
|
+
if retries==0:
|
59
|
+
return self.__getOrPost(True,ignoreCerts)
|
60
|
+
return self.connectionSafe.run(retries,0,self.__getOrPost,True,ignoreCerts)
|
61
|
+
|
62
|
+
def doPost(self, postData:dict, retries:int,ignoreCerts:bool=False)->HttpResponseBean:
|
63
|
+
"""
|
64
|
+
Esegue una richiesta di tipo POST
|
65
|
+
"""
|
66
|
+
self.postData=postData
|
67
|
+
if retries==0:
|
68
|
+
return self.__getOrPost(False,ignoreCerts)
|
69
|
+
return self.connectionSafe.run(retries,0,self.__getOrPost,False,ignoreCerts)
|
70
|
+
|
71
|
+
@catchAndRaiseHttpExceptions
|
72
|
+
def __getOrPost(self, isGet:bool,ignoreCerts:bool)->HttpResponseBean:
|
73
|
+
if not isGet:
|
74
|
+
postData=urlencode(self.postData).encode()
|
75
|
+
self.__requestObj.data=postData
|
76
|
+
# ignoro certificati
|
77
|
+
ctx=None
|
78
|
+
if ignoreCerts:
|
79
|
+
ctx=ssl.create_default_context()
|
80
|
+
ctx.check_hostname=False
|
81
|
+
ctx.verify_mode=ssl.CERT_NONE
|
82
|
+
with urllib.request.urlopen(self.__requestObj,context=ctx) as response:
|
83
|
+
responseBody=response.read()
|
84
|
+
responseHeaders=response.info()
|
85
|
+
status=response.status
|
86
|
+
# se il server manda il contenuto in formato compresso lo decomprimo
|
87
|
+
contentEncoding=responseHeaders.get('Content-Encoding')
|
88
|
+
if contentEncoding=="gzip":
|
89
|
+
responseBody=gzip.decompress(responseBody)
|
90
|
+
elif contentEncoding=="br":
|
91
|
+
responseBody=brotli.decompress(responseBody)
|
92
|
+
elif contentEncoding is not None and contentEncoding!= "deflate":
|
93
|
+
raise EccezioneRuntime("Codifica '"+contentEncoding+"' non gestita.")
|
94
|
+
return HttpResponseBean(status,responseBody,responseHeaders)
|
95
|
+
|
96
|
+
# opzioni aggiuntive
|
97
|
+
def addHeader(self,nome:str,valore):
|
98
|
+
if ModuloStringhe.isEmpty(valore):
|
99
|
+
return
|
100
|
+
self.__requestObj.add_header(nome,valore)
|
101
|
+
|
102
|
+
def setUserAgent(self,userAgent:str=None):
|
103
|
+
if ModuloStringhe.isEmpty(userAgent):
|
104
|
+
userAgent=self.UA_ANDROID
|
105
|
+
else:
|
106
|
+
userAgent=userAgent.strip()
|
107
|
+
self.addHeader('User-Agent',userAgent)
|
108
|
+
|
109
|
+
def setDownloadRange(self,inizio:int,fine:int):
|
110
|
+
self.addHeader('Range',"bytes="+str(inizio)+"-"+str(fine))
|
111
|
+
|
112
|
+
def setAuthenticationBasic(self,username:str,password:str):
|
113
|
+
authStr=ModuloBase64.codificaStr('%s:%s' % (username, password))
|
114
|
+
self.addHeader('Authorization',"Basic "+authStr)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import socket
|
2
|
+
import time
|
3
|
+
from urllib.error import URLError
|
4
|
+
|
5
|
+
import requests
|
6
|
+
|
7
|
+
from modulitiz_micro.ModuloFunzioni import ModuloFunzioni
|
8
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
9
|
+
from modulitiz_micro.eccezioni.http.EccezioneHttpGeneric import EccezioneHttpGeneric
|
10
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
11
|
+
|
12
|
+
|
13
|
+
class ModuloHttpConnectionSafe(object):
|
14
|
+
"""
|
15
|
+
Utility che serve per gestire eventuali errori di rete e riprova a fare la richiesta in caso di errore
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self,logger: ModuloLogging|None):
|
19
|
+
self.__logger=logger
|
20
|
+
|
21
|
+
self.isLoggerEnabled=self.__logger is not None
|
22
|
+
self.contaTentativi=0
|
23
|
+
self.nomeFunzChiamanti=[]
|
24
|
+
self.msgFunzChiamanti=None
|
25
|
+
|
26
|
+
def run(self, retries:int,retriesBeforeNotify: int, funzione, *args,**kwargs):
|
27
|
+
"""
|
28
|
+
se 'retries' e' = -1 allora continua a provare all'infinito
|
29
|
+
se 'retriesBeforeNotify' e' = 0 allora in caso di errore di rete notifica subito
|
30
|
+
"""
|
31
|
+
ritorno = None
|
32
|
+
while self._buildIf(self.contaTentativi,retries):
|
33
|
+
try:
|
34
|
+
ritorno = funzione(*args,**kwargs)
|
35
|
+
self.contaTentativi = -1
|
36
|
+
except (EccezioneHttpGeneric,ConnectionError, TimeoutError, URLError,
|
37
|
+
requests.exceptions.ConnectionError, socket.gaierror) as ex:
|
38
|
+
# controllo il tipo di errore
|
39
|
+
ModuloHttpConnectionSafe.__checkCodeExc(ex)
|
40
|
+
# gestisco l'assenza di connessione
|
41
|
+
self.contaTentativi += 1
|
42
|
+
# scrivo un messaggio di avviso
|
43
|
+
if retriesBeforeNotify == 0 or self.contaTentativi >= retriesBeforeNotify:
|
44
|
+
self.__buildMsgAndNotify()
|
45
|
+
ModuloHttpConnectionSafe.__dynamicPauseOrRaiseExc(self.contaTentativi, ex)
|
46
|
+
self.contaTentativi=0
|
47
|
+
self.nomeFunzChiamanti.clear()
|
48
|
+
return ritorno
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def _buildIf(contaTentativi:int,retries:int)->bool:
|
52
|
+
return contaTentativi >= 0 and (retries==-1 or contaTentativi<retries)
|
53
|
+
|
54
|
+
def __buildMsgAndNotify(self):
|
55
|
+
if not self.nomeFunzChiamanti:
|
56
|
+
self.msgFunzChiamanti="Errore di connessione nella funzione "+ModuloFunzioni.getFunctionName(1)+"()"
|
57
|
+
i=0
|
58
|
+
while -1<i<2:
|
59
|
+
nomeFunz = ModuloFunzioni.getFunctionName(i+2)
|
60
|
+
if not ModuloStringhe.isEmpty(nomeFunz):
|
61
|
+
self.msgFunzChiamanti += f", chiamata da {nomeFunz}()"
|
62
|
+
i+=1
|
63
|
+
else:
|
64
|
+
i=-1
|
65
|
+
msg=self.msgFunzChiamanti+f"; tentativo numero: {self.contaTentativi}"
|
66
|
+
if self.isLoggerEnabled:
|
67
|
+
self.__logger.error(msg)
|
68
|
+
else:
|
69
|
+
print(msg)
|
70
|
+
|
71
|
+
@staticmethod
|
72
|
+
def __checkCodeExc(ex: Exception):
|
73
|
+
try:
|
74
|
+
codiceErrore = ex.code
|
75
|
+
except AttributeError:
|
76
|
+
codiceErrore = 0
|
77
|
+
if codiceErrore in (404, 500):
|
78
|
+
raise ex
|
79
|
+
|
80
|
+
@staticmethod
|
81
|
+
def __dynamicPauseOrRaiseExc(contaTentativi: int, ex: Exception):
|
82
|
+
if 0 < contaTentativi <= 20:
|
83
|
+
time.sleep(10)
|
84
|
+
elif 20 < contaTentativi <= 30:
|
85
|
+
time.sleep(60)
|
86
|
+
elif 20 < contaTentativi <= 50:
|
87
|
+
time.sleep(60 * 2)
|
88
|
+
elif 50 < contaTentativi <= 100:
|
89
|
+
time.sleep(60 * 5)
|
90
|
+
else:
|
91
|
+
raise ex
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import json
|
2
|
+
import random
|
3
|
+
from urllib.parse import quote_plus
|
4
|
+
from urllib.parse import unquote_plus
|
5
|
+
|
6
|
+
import requests
|
7
|
+
|
8
|
+
from modulitiz_micro.ModuloStringhe import ModuloStringhe
|
9
|
+
from modulitiz_micro.eccezioni.http.EccezioneHttpGeneric import EccezioneHttpGeneric
|
10
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
11
|
+
from modulitiz_micro.rete.http.ModuloHttp import ModuloHttp
|
12
|
+
from modulitiz_micro.rete.http.ModuloHttpConnectionSafe import ModuloHttpConnectionSafe
|
13
|
+
from modulitiz_micro.rete.http.decorators.catchAndRaiseHttpExceptions import catchAndRaiseHttpExceptions
|
14
|
+
|
15
|
+
|
16
|
+
class ModuloHttpUtils(object):
|
17
|
+
@staticmethod
|
18
|
+
def isUrlAvailable(logger:ModuloLogging|None,url:str,retries:int,ignoreCerts:bool=False) -> bool:
|
19
|
+
if retries!=0:
|
20
|
+
connectionSafe=ModuloHttpConnectionSafe(logger)
|
21
|
+
return connectionSafe.run(retries,0,ModuloHttpUtils.__isUrlAvailable,url,ignoreCerts)
|
22
|
+
try:
|
23
|
+
return ModuloHttpUtils.__isUrlAvailable(url,ignoreCerts)
|
24
|
+
except EccezioneHttpGeneric:
|
25
|
+
return False
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
@catchAndRaiseHttpExceptions
|
29
|
+
def __isUrlAvailable(url:str,ignoreCerts:bool) -> bool:
|
30
|
+
with requests.get(url,stream=True,verify=(not ignoreCerts)) as response:
|
31
|
+
return response.status_code==ModuloHttp.STATUS_OK
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def getIp() -> str|None:
|
35
|
+
url=random.choice(ModuloHttp.URLS_GET_IP)
|
36
|
+
http=ModuloHttp(url,None)
|
37
|
+
try:
|
38
|
+
response=http.doGet(2,False)
|
39
|
+
except EccezioneHttpGeneric:
|
40
|
+
return None
|
41
|
+
if response is None:
|
42
|
+
return None
|
43
|
+
return response.responseBody.decode(ModuloStringhe.CODIFICA_UTF8)
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def translate(cls,langFrom:str|None,langTo:str,msg:str) -> str|None:
|
47
|
+
if langFrom is None:
|
48
|
+
langFrom="auto"
|
49
|
+
msg=cls.encodeUrl(msg)
|
50
|
+
url=f'https://translate.googleapis.com/translate_a/single?client=gtx&sl={langFrom}&tl={langTo}&dt=t&q={msg}'
|
51
|
+
http=ModuloHttp(url,None)
|
52
|
+
try:
|
53
|
+
response=http.doGet(3,False)
|
54
|
+
except EccezioneHttpGeneric:
|
55
|
+
return None
|
56
|
+
responseText=response.responseBody.decode(ModuloStringhe.CODIFICA_UTF8)
|
57
|
+
result=json.loads(responseText)
|
58
|
+
output="".join(x[0] for x in result[0])
|
59
|
+
return output
|
60
|
+
|
61
|
+
@staticmethod
|
62
|
+
def encodeUrl(url: str) -> str:
|
63
|
+
return quote_plus(url)
|
64
|
+
@staticmethod
|
65
|
+
def decodeUrl(url: str) -> str:
|
66
|
+
return unquote_plus(url)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import socket
|
2
|
+
from functools import wraps
|
3
|
+
from urllib.error import URLError
|
4
|
+
|
5
|
+
import requests
|
6
|
+
|
7
|
+
from modulitiz_micro.eccezioni.http.EccezioneHttpGeneric import EccezioneHttpGeneric
|
8
|
+
|
9
|
+
|
10
|
+
def catchAndRaiseHttpExceptions(funzione):
|
11
|
+
"""
|
12
|
+
Cattura tutte le eccezioni http di vario tipo e rilancia un'eccezione custom
|
13
|
+
"""
|
14
|
+
|
15
|
+
@wraps(funzione)
|
16
|
+
def wrapped(*args,**kwargs):
|
17
|
+
try:
|
18
|
+
return funzione(*args,**kwargs)
|
19
|
+
except (ConnectionError,TimeoutError,URLError,
|
20
|
+
requests.exceptions.ConnectionError,socket.gaierror) as ex:
|
21
|
+
raise EccezioneHttpGeneric() from ex
|
22
|
+
return wrapped
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import socket
|
2
|
+
import struct
|
3
|
+
from abc import ABC
|
4
|
+
|
5
|
+
from modulitiz_micro.ModuloDate import ModuloDate
|
6
|
+
from modulitiz_micro.eccezioni.http.EccezioneHttpGeneric import EccezioneHttpGeneric
|
7
|
+
from modulitiz_micro.files.ModuloLogging import ModuloLogging
|
8
|
+
from modulitiz_micro.rete.http.ModuloHttpConnectionSafe import ModuloHttpConnectionSafe
|
9
|
+
from modulitiz_micro.rete.http.decorators.catchAndRaiseHttpExceptions import catchAndRaiseHttpExceptions
|
10
|
+
|
11
|
+
|
12
|
+
class AbstractModuloNtp(ABC):
|
13
|
+
REFRESH_CACHE_EVERY_SECS=4*3600*1000 # frequenza richiesta NTP, l'rpi sballa di 45 minuti ogni 24 ore
|
14
|
+
OFFSET_MILLIS=1 # offset correzione errore
|
15
|
+
TIMEOUT=10
|
16
|
+
|
17
|
+
__PORTA=123 # numero della porta (UDP)
|
18
|
+
__BUF=1024
|
19
|
+
__TIME1970=2208988800 # reference time, secondi da 1900-01-01 00:00:00 a 1970-01-01 00:00:00 = 70 anni
|
20
|
+
__MSG=('\x1b' + 47 * '\0').encode()
|
21
|
+
|
22
|
+
|
23
|
+
def __init__(self,host:str,logger:ModuloLogging):
|
24
|
+
self.logger=logger
|
25
|
+
self.connectionSafe = ModuloHttpConnectionSafe(logger)
|
26
|
+
self.host=host
|
27
|
+
self.lastNtpResponse=0
|
28
|
+
self.lastNtpTimestampUtc=0
|
29
|
+
|
30
|
+
def getTimestampUtcFromNtp(self,useCache:bool=True,retryIfError:bool=True)->int:
|
31
|
+
if useCache is True and self.lastNtpResponse!=0:
|
32
|
+
# controllo se e' passato poco tempo dall'ultima chiamata ntp
|
33
|
+
diffMillisLastResponse=(ModuloDate.getMillis()-self.lastNtpResponse)
|
34
|
+
if diffMillisLastResponse<self.REFRESH_CACHE_EVERY_SECS:
|
35
|
+
# calcolo l'ora attuale usando il cronometro interno
|
36
|
+
return self.lastNtpTimestampUtc+diffMillisLastResponse+self.OFFSET_MILLIS
|
37
|
+
# invio la richiesta e in caso di errore...
|
38
|
+
if not retryIfError:
|
39
|
+
#... uso il cronometro interno
|
40
|
+
try:
|
41
|
+
timestampUtc=self.__ntpSendPacket()
|
42
|
+
except EccezioneHttpGeneric:
|
43
|
+
timestampUtc=None
|
44
|
+
else:
|
45
|
+
#... riprovo
|
46
|
+
timestampUtc=self.connectionSafe.run(1,3,self.__ntpSendPacket)
|
47
|
+
if timestampUtc is None:
|
48
|
+
diffMillisLastResponse=(ModuloDate.getMillis()-self.lastNtpResponse)
|
49
|
+
return self.lastNtpTimestampUtc+diffMillisLastResponse+self.OFFSET_MILLIS
|
50
|
+
timestampUtc=timestampUtc*1000
|
51
|
+
# successo
|
52
|
+
self.lastNtpResponse=ModuloDate.getMillis()
|
53
|
+
self.lastNtpTimestampUtc=timestampUtc
|
54
|
+
return self.lastNtpTimestampUtc
|
55
|
+
|
56
|
+
def getDatetimeFromNtp(self,useCache:bool=True,retryIfError:bool=True):
|
57
|
+
tsSec=self.getTimestampUtcFromNtp(useCache,retryIfError)//1000
|
58
|
+
return ModuloDate.timestampUtcToDate(tsSec)
|
59
|
+
|
60
|
+
@catchAndRaiseHttpExceptions
|
61
|
+
def __ntpSendPacket(self)->int:
|
62
|
+
# connect to server
|
63
|
+
client=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
64
|
+
client.settimeout(self.TIMEOUT)
|
65
|
+
client.sendto(self.__MSG,(self.host,self.__PORTA))
|
66
|
+
# ricevo i dati
|
67
|
+
response=client.recvfrom(self.__BUF)[0]
|
68
|
+
# chiudo la socket
|
69
|
+
client.close()
|
70
|
+
# estraggo il timestamp
|
71
|
+
tsSec=struct.unpack("!12I",response)[10]
|
72
|
+
tsSec-=self.__TIME1970
|
73
|
+
return tsSec
|