first commit
This commit is contained in:
commit
138ea21fd3
161
README.md
Normal file
161
README.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
## NordVPN systray
|
||||||
|
|
||||||
|
### Développement
|
||||||
|
|
||||||
|
Prérequis, l'application nordvpn est installée
|
||||||
|
|
||||||
|
* Le **dossier développement** qui contient tous les modules nécessaire ainsi que les binaires
|
||||||
|
`/srv/media/dplus/python-dev/nordvpntray`
|
||||||
|
* Le **dossier final après installation**
|
||||||
|
$HOME/.local/share/nordvpntray
|
||||||
|
|
||||||
|
Créer un projet python `nordvpntray`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Crée le dossier si inexistant
|
||||||
|
mkdir $HOME/media/dplus/python-dev/nordvpntray
|
||||||
|
# aller dans le dossier
|
||||||
|
cd $HOME/media/dplus/python-dev/nordvpntray
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour créer un environnement, utilisez la commande `python -m venv <environment name>`
|
||||||
|
Créer un environnement pour l'application
|
||||||
|
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
activer l'environnement virtuel
|
||||||
|
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
On arrive sur un prompt : `(venv) [yann@PC1 nordvpntray]$`
|
||||||
|
|
||||||
|
Mettre à jour pip dans l'environnement
|
||||||
|
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
Les modules à installer
|
||||||
|
|
||||||
|
fichier requirements.txt
|
||||||
|
|
||||||
|
```
|
||||||
|
pystray
|
||||||
|
pillow
|
||||||
|
ping3
|
||||||
|
plyer
|
||||||
|
desktop-notifier
|
||||||
|
dbus-python
|
||||||
|
wheel
|
||||||
|
urllib3
|
||||||
|
PyGObject
|
||||||
|
```
|
||||||
|
|
||||||
|
Installer les modules supplémentaires
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
Projet sur éditeur python "Wing Personal 10"
|
||||||
|
$HOME/media/dplus/python-dev/nordvpntray.wpr
|
||||||
|
|
||||||
|
Créer les scripts
|
||||||
|
|
||||||
|
Aller dans le dossier développement
|
||||||
|
|
||||||
|
cd /srv/media/dplus/python-dev/nordvpntray
|
||||||
|
|
||||||
|
Créer un script de lancement : `nordvpntray.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lancement nordvpntray
|
||||||
|
# Dossier travail
|
||||||
|
cd $HOME/.local/share/nordvpntray
|
||||||
|
# Chemin
|
||||||
|
export PATH="$HOME/.local/share/nordvpntray/venv/bin:$PATH"
|
||||||
|
# Exécution script
|
||||||
|
python nordvpntray.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Le rendre exécutable
|
||||||
|
|
||||||
|
chmod +x nordvpntray.sh
|
||||||
|
|
||||||
|
Créer icône bureau `nordvpntray.desktop`
|
||||||
|
|
||||||
|
```
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
Name=NordVPN Graphique
|
||||||
|
Comment=Etat du service nordvpn
|
||||||
|
Icon=nordvpn
|
||||||
|
Exec=nordvpntray
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script d'installation `install.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# copie dossier
|
||||||
|
cp -r ../nordvpntray $HOME/.local/share/
|
||||||
|
# Icône bureau
|
||||||
|
mkdir -p $HOME/.local/share/applications/
|
||||||
|
cp nordvpntray.desktop $HOME/.local/share/applications/
|
||||||
|
# on met le script en lien dans /usr/local/bin/
|
||||||
|
sudo ln -s $HOME/.local/share/nordvpntray/nordvpntray.sh /usr/local/bin/nordvpntray
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installer NordVPN systray
|
||||||
|
|
||||||
|
### Prérequis
|
||||||
|
|
||||||
|
L'application NordVPN doit être installée, suivre la procédure : [Application NordVPN](https://static.rnmkcy.eu/2024/11/23/NordVPN.html)
|
||||||
|
|
||||||
|
Les paramètres à activer ou désactiver
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nordvpn set autoconnect disabled
|
||||||
|
nordvpn set tray disabled
|
||||||
|
nordvpn set notify disabled
|
||||||
|
nordvpn set ipv6 enabled
|
||||||
|
nordvpn set lan-discovery enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Paramétrage : `nordvpn settings`
|
||||||
|
|
||||||
|
```
|
||||||
|
Technology: NORDLYNX
|
||||||
|
Firewall: enabled
|
||||||
|
Firewall Mark: 0xe1f1
|
||||||
|
Routing: enabled
|
||||||
|
Analytics: enabled
|
||||||
|
Kill Switch: disabled
|
||||||
|
Threat Protection Lite: disabled
|
||||||
|
Notify: disabled
|
||||||
|
Tray: disabled
|
||||||
|
Auto-connect: disabled
|
||||||
|
IPv6: enabled
|
||||||
|
Meshnet: disabled
|
||||||
|
DNS: disabled
|
||||||
|
LAN Discovery: enabled
|
||||||
|
Virtual Location: enabled
|
||||||
|
Post-quantum VPN: disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### nordvpntray
|
||||||
|
|
||||||
|
Cloner le dépot
|
||||||
|
|
||||||
|
git clone https://gitea.rnmkcy.eu/yann/nordvpntray
|
||||||
|
|
||||||
|
cd nordvpntray
|
||||||
|
./install.sh
|
||||||
|
|
||||||
|
Lancement en début de session XFCE
|
||||||
|
|
||||||
|
* Paramètres --> Gestionnaire de Paramètres --> Session et démarrage
|
||||||
|
* Démarrage automatique d'application
|
||||||
|
* +Ajouter
|
||||||
|
* Nom: nordvpntray
|
||||||
|
* Description : NordVPN Graphique
|
||||||
|
* Commande: nordvpntray
|
||||||
|
* Déclencher à la connexion
|
||||||
|
* OK
|
||||||
|
* Fermer
|
BIN
img/dns-logo.png
Executable file
BIN
img/dns-logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
BIN
img/nord-logo-rouge.png
Normal file
BIN
img/nord-logo-rouge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
img/nord-logo-vert.png
Normal file
BIN
img/nord-logo-vert.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
img/nord-logo.png
Normal file
BIN
img/nord-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
img/nordvpn.jpg
Normal file
BIN
img/nordvpn.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
8
install.sh
Executable file
8
install.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
# copie dossier
|
||||||
|
cp -r ../nordvpntray $HOME/.local/share/
|
||||||
|
# Icône bureau
|
||||||
|
mkdir -p $HOME/.local/share/applications/
|
||||||
|
cp nordvpntray.desktop $HOME/.local/share/applications/
|
||||||
|
# on met le script en lien dans /usr/local/bin/
|
||||||
|
sudo ln -s $HOME/.local/share/nordvpntray/nordvpntray.sh /usr/local/bin/nordvpntray
|
||||||
|
|
BIN
nord-logo.png
Normal file
BIN
nord-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
7
nordvpntray.desktop
Normal file
7
nordvpntray.desktop
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
Name=NordVPN Graphique
|
||||||
|
Comment=Etat du service nordvpn
|
||||||
|
Icon=nordvpn
|
||||||
|
Exec=nordvpn sh $HOME/.local/share/nordvpntray/nordvpntray.sh
|
304
nordvpntray.py
Executable file
304
nordvpntray.py
Executable file
@ -0,0 +1,304 @@
|
|||||||
|
import pystray
|
||||||
|
from PIL import Image
|
||||||
|
import threading # Import the threading module for creating threads
|
||||||
|
|
||||||
|
from desktop_notifier import DesktopNotifier, Urgency, Button, ReplyField, DEFAULT_SOUND
|
||||||
|
from plyer import notification
|
||||||
|
|
||||||
|
from urllib.request import Request
|
||||||
|
from urllib.request import urlopen
|
||||||
|
from urllib.request import URLError
|
||||||
|
import sys,os
|
||||||
|
import gobject
|
||||||
|
import json
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
from random import randint
|
||||||
|
from platform import system as system_name
|
||||||
|
from subprocess import call as system_call
|
||||||
|
|
||||||
|
def ping(host):
|
||||||
|
fn = open(os.devnull, 'w')
|
||||||
|
param = '-n' if system_name().lower() == 'windows' else '-c'
|
||||||
|
command = ['ping', param, '1', host]
|
||||||
|
retcode = system_call(command, stdout=fn, stderr=subprocess.STDOUT)
|
||||||
|
fn.close()
|
||||||
|
return retcode == 0
|
||||||
|
|
||||||
|
def desktop_notifier(title, message, timeout=10, app_icon=None):
|
||||||
|
notification.notify(
|
||||||
|
title=title,
|
||||||
|
message=message,
|
||||||
|
timeout=timeout,
|
||||||
|
app_icon=app_icon,
|
||||||
|
)
|
||||||
|
|
||||||
|
def dns_leak():
|
||||||
|
# Test fuites DNS
|
||||||
|
leak_id = randint(1000000, 9999999)
|
||||||
|
for x in range(0, 10):
|
||||||
|
ping('.'.join([str(x), str(leak_id), "bash.ws"]))
|
||||||
|
|
||||||
|
response = urlopen("https://bash.ws/dnsleak/test/"+str(leak_id)+"?json")
|
||||||
|
dnsleak = "https://bash.ws/dnsleak/test/"+str(leak_id)+"?json" + '\n '
|
||||||
|
data = response.read().decode("utf-8")
|
||||||
|
parsed_data = json.loads(data)
|
||||||
|
|
||||||
|
dnsleak = dnsleak + '\n ' + "Votre IP "
|
||||||
|
for dns_server in parsed_data:
|
||||||
|
if dns_server['type'] == "ip":
|
||||||
|
if dns_server['country_name']:
|
||||||
|
if dns_server['asn']:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n ' + " [" + dns_server['country_name'] + ", " + dns_server['asn'] + "]" + '\n '
|
||||||
|
else:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n ' + " [" + dns_server['country_name'] +" ]" + '\n '
|
||||||
|
else:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n '
|
||||||
|
|
||||||
|
servers = 0
|
||||||
|
for dns_server in parsed_data:
|
||||||
|
if dns_server['type'] == "dns":
|
||||||
|
servers = servers + 1
|
||||||
|
|
||||||
|
if servers == 0:
|
||||||
|
dnsleak = dnsleak + "No DNS servers found"
|
||||||
|
else:
|
||||||
|
dnsleak = dnsleak + '\n ' + "Vous utilisez " + str(servers) + " serveur(s) DNS "
|
||||||
|
for dns_server in parsed_data:
|
||||||
|
if dns_server['type'] == "dns":
|
||||||
|
if dns_server['country_name']:
|
||||||
|
if dns_server['asn']:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n ' + " [" + dns_server['country_name'] + ", " + dns_server['asn'] + "]" + '\n '
|
||||||
|
else:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n ' + " [" + dns_server['country_name'] + "]" + '\n '
|
||||||
|
else:
|
||||||
|
dnsleak = dnsleak + dns_server['ip'] + '\n '
|
||||||
|
|
||||||
|
dnsleak = dnsleak + '\n ' + "Conclusion:"
|
||||||
|
for dns_server in parsed_data:
|
||||||
|
if dns_server['type'] == "conclusion":
|
||||||
|
if dns_server['ip']:
|
||||||
|
dnsleak = dnsleak + dns_server['ip']
|
||||||
|
# Retour dnsleak
|
||||||
|
# print(dnsleak)
|
||||||
|
return dnsleak
|
||||||
|
|
||||||
|
def NordVPN(commande, option=""):
|
||||||
|
global connexion, stest
|
||||||
|
match commande:
|
||||||
|
case "status":
|
||||||
|
sortie=os.popen("nordvpn status", "r").read()
|
||||||
|
titre="Statut VPN"
|
||||||
|
case "settings":
|
||||||
|
sortie=os.popen("nordvpn settings", "r").read()
|
||||||
|
titre="Paramètres VPN"
|
||||||
|
case "test":
|
||||||
|
sortie="Pour les tests"
|
||||||
|
titre="TESTS"
|
||||||
|
#desktop_notifier(titre, "Ceci est test",10,images + "nordvpn.jpg")
|
||||||
|
if stest:
|
||||||
|
stest = False
|
||||||
|
icon.icon = connected_img
|
||||||
|
else:
|
||||||
|
stest = True
|
||||||
|
icon.icon = disconnected_img
|
||||||
|
case "connect":
|
||||||
|
titre="Connexion VPN"
|
||||||
|
if not connexion:
|
||||||
|
sortie=os.popen("nordvpn connect", "r").read()
|
||||||
|
connexion = True
|
||||||
|
print(sortie)
|
||||||
|
icon.icon = connected_img
|
||||||
|
cmdcli=os.popen("nordvpn status", "r").read()
|
||||||
|
if not "Disconnected" in cmdcli:
|
||||||
|
# VPN actif
|
||||||
|
for line in cmdcli.split('\n'):
|
||||||
|
if "Server:" in line:
|
||||||
|
# Extraire le serveur avec suppression espaces début fin
|
||||||
|
serveur=line[len("Server:"):].strip()
|
||||||
|
icon.title=serveur
|
||||||
|
else:
|
||||||
|
print(sortie)
|
||||||
|
sortie="Connexion VPN active"
|
||||||
|
case "disconnect":
|
||||||
|
titre="Déconnexion VPN"
|
||||||
|
sortie=os.popen("nordvpn disconnect", "r").read()
|
||||||
|
if "You are not connected to NordVPN" in sortie:
|
||||||
|
sortie="Vous n'êtes pas connecté à NordVPN"
|
||||||
|
print(sortie)
|
||||||
|
connexion = False
|
||||||
|
print(sortie)
|
||||||
|
icon.icon = disconnected_img
|
||||||
|
icon.title="Déconnecté"
|
||||||
|
case "login":
|
||||||
|
titre="Connexion Compte"
|
||||||
|
if not connexion:
|
||||||
|
sortie=os.popen("nordvpn login", "r").read()
|
||||||
|
else:
|
||||||
|
sortie=os.popen("nordvpn account", "r").read()
|
||||||
|
case "dnsleak":
|
||||||
|
# appel fonction et affichage
|
||||||
|
titre="DNS Leak test (fuites)"
|
||||||
|
sortie=dns_leak()
|
||||||
|
case "countries":
|
||||||
|
# groupes de serveur
|
||||||
|
titre="Groupes de serveur"
|
||||||
|
sortie=os.popen("nordvpn countries", "r").read()
|
||||||
|
case "groups":
|
||||||
|
# groupes de serveur
|
||||||
|
titre="Groupes de serveur"
|
||||||
|
sortie=os.popen("nordvpn groups", "r").read()
|
||||||
|
case "version":
|
||||||
|
titre="Version NordVPN"
|
||||||
|
sortie=os.popen("nordvpn version", "r").read()
|
||||||
|
case "_":
|
||||||
|
print("Oops, quelque chose s'est mal passé 🤯")
|
||||||
|
|
||||||
|
# analyse résultat des commandes bash nordvpn
|
||||||
|
print(sortie)
|
||||||
|
message = sortie.replace("Server", "Serveur")
|
||||||
|
message = message.replace("Hostname", "Nom hôte")
|
||||||
|
message = message.replace("Country", "Pays")
|
||||||
|
message = message.replace("City", "Ville")
|
||||||
|
message = message.replace("Current technology", "Technologie actuelle")
|
||||||
|
message = message.replace("Current protocol", "Protocole actuel")
|
||||||
|
message = message.replace("Transfer", "Transfert")
|
||||||
|
message = message.replace("Uptime", "Temps de fonction")
|
||||||
|
message = message.replace("Connected", "Connecté")
|
||||||
|
message = message.replace("Disconnected", "Déconnecté")
|
||||||
|
message = message.replace("second", "seconde")
|
||||||
|
message = message.replace("Disabled", "Inactif")
|
||||||
|
message = message.replace("Enabled", "Actif")
|
||||||
|
message = message.replace("disabled", "Inactif")
|
||||||
|
message = message.replace("enabled", "Actif")
|
||||||
|
message = message.replace("Connecting to", "Se connecter à")
|
||||||
|
message = message.replace("You are connected to", "Vous êtes connecté à")
|
||||||
|
if commande=="disconnect":
|
||||||
|
message="Vous êtes déconnecté de NordVPN"
|
||||||
|
desktop_notifier(titre, message,10,images + "nordvpn.jpg")
|
||||||
|
#print(message)
|
||||||
|
|
||||||
|
|
||||||
|
def after_click(icon, query):
|
||||||
|
match str(query):
|
||||||
|
case "Status":
|
||||||
|
NordVPN("status")
|
||||||
|
case "Paramètres":
|
||||||
|
NordVPN("settings")
|
||||||
|
case "Connexion VPN rapide":
|
||||||
|
NordVPN("connect")
|
||||||
|
case "Déconnexion VPN":
|
||||||
|
NordVPN("disconnect")
|
||||||
|
case "Fuites DNS":
|
||||||
|
NordVPN("dnsleak")
|
||||||
|
case "Connexion/Infos Compte":
|
||||||
|
NordVPN("login")
|
||||||
|
case "TEST":
|
||||||
|
NordVPN("test")
|
||||||
|
case "Groupes de serveurs":
|
||||||
|
NordVPN("groups")
|
||||||
|
case "Pays":
|
||||||
|
NordVPN("countries")
|
||||||
|
case "Version":
|
||||||
|
NordVPN("version")
|
||||||
|
case "Quitter":
|
||||||
|
icon.stop()
|
||||||
|
|
||||||
|
def vpnactif():
|
||||||
|
# Tester si la connexion VPN est active
|
||||||
|
cmdcli=os.popen("nordvpn status", "r").read()
|
||||||
|
print(cmdcli)
|
||||||
|
if not "Disconnected" in cmdcli:
|
||||||
|
# VPN actif
|
||||||
|
for line in cmdcli.split('\n'):
|
||||||
|
if "Server:" in line:
|
||||||
|
# Extraire le serveur avec suppression espaces début fin
|
||||||
|
serveur=line[len("Server:"):].strip()
|
||||||
|
icon.title=serveur
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# VPN inactif
|
||||||
|
icon.title="VPN inactif"
|
||||||
|
return False
|
||||||
|
|
||||||
|
def on_clicked(icon, item):
|
||||||
|
global state
|
||||||
|
state = not item.checked
|
||||||
|
|
||||||
|
'''
|
||||||
|
# Départ programme
|
||||||
|
On regarde si le service nordvpnd est actif
|
||||||
|
'''
|
||||||
|
|
||||||
|
# récupérer le chemin du répertoire courant
|
||||||
|
chemin_courant = os.getcwd() + "/"
|
||||||
|
print("Le répertoire courant est : " + chemin_courant )
|
||||||
|
images = chemin_courant + "img/"
|
||||||
|
print("Le répertoire images est : " + images )
|
||||||
|
|
||||||
|
state = False
|
||||||
|
|
||||||
|
# on regarde si le service nordvpnd est actif pour afficher la couleur du bouton
|
||||||
|
etat=os.popen("systemctl is-active nordvpnd.service").read()
|
||||||
|
if 'inactive' in etat:
|
||||||
|
# nordvpnd inactif
|
||||||
|
print("Service nordvpnd inactif")
|
||||||
|
desktop_notifier("Service nordvpnd", "Le service NordVPN daemon est inactif",10,images + "nordvpn.jpg")
|
||||||
|
# On arrête le script
|
||||||
|
sys.exit("Service nordvpnd inactif")
|
||||||
|
else:
|
||||||
|
print("Service nordvpnd actif")
|
||||||
|
|
||||||
|
# Définir les images pour le systray
|
||||||
|
connected_img = Image.open(images + "nord-logo-vert.png") # image connexion NordVPN active
|
||||||
|
disconnected_img = Image.open(images + "nord-logo-rouge.png") # image connexion NordVPN inactive
|
||||||
|
|
||||||
|
stest = True
|
||||||
|
|
||||||
|
icon = pystray.Icon("VPN")
|
||||||
|
|
||||||
|
# Test si connexion active
|
||||||
|
if vpnactif():
|
||||||
|
# VPN actif
|
||||||
|
connexion = True
|
||||||
|
icon.icon = connected_img
|
||||||
|
print("VPN actif")
|
||||||
|
else:
|
||||||
|
# VPN inactif
|
||||||
|
connexion = False
|
||||||
|
icon.icon = disconnected_img
|
||||||
|
icon.title="Déconnecté"
|
||||||
|
print("VPN inactif")
|
||||||
|
|
||||||
|
icon.menu= pystray.Menu(
|
||||||
|
pystray.MenuItem("Status",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Paramètres",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Fuites DNS",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Connexion VPN rapide",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Déconnexion VPN",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Connexion/Infos Compte",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("TEST",
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem("Listes",
|
||||||
|
pystray.Menu(
|
||||||
|
pystray.MenuItem('Groupes de serveurs',
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem('Pays',
|
||||||
|
after_click),
|
||||||
|
pystray.MenuItem('On/Off',
|
||||||
|
on_clicked,checked=lambda item: state),
|
||||||
|
pystray.MenuItem('Version',
|
||||||
|
after_click)
|
||||||
|
|
||||||
|
)),
|
||||||
|
pystray.MenuItem("Quitter",
|
||||||
|
after_click)
|
||||||
|
)
|
||||||
|
|
||||||
|
icon.run()
|
7
nordvpntray.sh
Executable file
7
nordvpntray.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
# Lancement nordvpntray
|
||||||
|
# Dossier travail
|
||||||
|
cd $HOME/.local/share/nordvpntray
|
||||||
|
# Chemin
|
||||||
|
export PATH="$HOME/.local/share/nordvpntray/venv/bin:$PATH"
|
||||||
|
# Exécution script
|
||||||
|
python nordvpntray.py
|
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
pystray
|
||||||
|
pillow
|
||||||
|
ping3
|
||||||
|
plyer
|
||||||
|
desktop-notifier
|
||||||
|
dbus-python
|
||||||
|
wheel
|
||||||
|
urllib3
|
||||||
|
PyGObject
|
Loading…
Reference in New Issue
Block a user