From cb62cb720d9f5586a27c2e410ed766256482e7b4 Mon Sep 17 00:00:00 2001 From: qo-op Date: Wed, 24 Feb 2021 16:13:19 +0100 Subject: [PATCH] jacklis upgrade with Ad publish support --- ajouter_video.sh | 40 ++- install.sh | 2 +- zen/jaklis.old/.env | 5 + zen/jaklis.old/.env.template | 6 + zen/jaklis.old/README.md | 76 +++++ zen/jaklis.old/jaklis.py | 226 +++++++++++++ .../lib/__pycache__/cesium.cpython-36.pyc | Bin 0 -> 3287 bytes .../lib/__pycache__/cesium.cpython-38.pyc | Bin .../__pycache__/cesiumCommon.cpython-36.pyc | Bin 0 -> 1725 bytes .../__pycache__/cesiumCommon.cpython-38.pyc | Bin .../lib/__pycache__/gva.cpython-36.pyc | Bin .../lib/__pycache__/gvaPay.cpython-36.pyc | Bin .../lib/__pycache__/likes.cpython-36.pyc | Bin 0 -> 6049 bytes .../lib/__pycache__/likes.cpython-38.pyc | Bin .../lib/__pycache__/messaging.cpython-36.pyc | Bin 0 -> 6763 bytes .../lib/__pycache__/messaging.cpython-38.pyc | Bin .../lib/__pycache__/natools.cpython-36.pyc | Bin 0 -> 9296 bytes .../lib/__pycache__/natools.cpython-38.pyc | Bin .../lib/__pycache__/profiles.cpython-36.pyc | Bin 0 -> 3044 bytes .../lib/__pycache__/profiles.cpython-38.pyc | Bin zen/jaklis.old/lib/cesium.py | 93 ++++++ zen/jaklis.old/lib/cesiumCommon.py | 51 +++ zen/jaklis.old/lib/crypt.py | 31 ++ zen/jaklis.old/lib/gva.py | 59 ++++ zen/jaklis.old/lib/gvaBalance.py | 50 +++ zen/jaklis.old/lib/gvaHistory.py | 209 ++++++++++++ zen/jaklis.old/lib/gvaPay.py | 172 ++++++++++ zen/jaklis.old/lib/likes.py | 242 ++++++++++++++ zen/jaklis.old/lib/messaging.py | 236 ++++++++++++++ zen/jaklis.old/lib/natools.py | 297 ++++++++++++++++++ zen/jaklis.old/lib/profiles.py | 125 ++++++++ zen/jaklis.old/paiements.py | 94 ++++++ zen/jaklis.old/requirements.txt | 7 + zen/jaklis.old/setup.sh | 12 + zen/jaklis/.env.template | 0 zen/jaklis/README.md | 2 + zen/jaklis/jaklis.py | 47 ++- .../lib/__pycache__/cesium.cpython-36.pyc | Bin 3287 -> 4118 bytes .../__pycache__/cesiumCommon.cpython-36.pyc | Bin 1725 -> 1725 bytes .../lib/__pycache__/likes.cpython-36.pyc | Bin 6049 -> 6049 bytes .../lib/__pycache__/messaging.cpython-36.pyc | Bin 6763 -> 6763 bytes .../lib/__pycache__/natools.cpython-36.pyc | Bin 9296 -> 9296 bytes .../lib/__pycache__/offers.cpython-36.pyc | Bin 0 -> 3538 bytes .../lib/__pycache__/profiles.cpython-36.pyc | Bin 3044 -> 3044 bytes zen/jaklis/lib/cesium.py | 26 ++ zen/jaklis/lib/cesiumCommon.py | 0 zen/jaklis/lib/gva.py | 4 +- zen/jaklis/lib/gvaBalance.py | 0 zen/jaklis/lib/gvaHistory.py | 151 ++++++--- zen/jaklis/lib/gvaPay.py | 0 zen/jaklis/lib/likes.py | 0 zen/jaklis/lib/messaging.py | 0 zen/jaklis/lib/offers.py | 137 ++++++++ zen/jaklis/lib/profiles.py | 0 zen/jaklis/requirements.txt | 1 + 55 files changed, 2324 insertions(+), 77 deletions(-) create mode 100644 zen/jaklis.old/.env create mode 100644 zen/jaklis.old/.env.template create mode 100644 zen/jaklis.old/README.md create mode 100755 zen/jaklis.old/jaklis.py create mode 100644 zen/jaklis.old/lib/__pycache__/cesium.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/cesium.cpython-38.pyc (100%) create mode 100644 zen/jaklis.old/lib/__pycache__/cesiumCommon.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/cesiumCommon.cpython-38.pyc (100%) rename zen/{jaklis => jaklis.old}/lib/__pycache__/gva.cpython-36.pyc (100%) rename zen/{jaklis => jaklis.old}/lib/__pycache__/gvaPay.cpython-36.pyc (100%) create mode 100644 zen/jaklis.old/lib/__pycache__/likes.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/likes.cpython-38.pyc (100%) create mode 100644 zen/jaklis.old/lib/__pycache__/messaging.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/messaging.cpython-38.pyc (100%) create mode 100644 zen/jaklis.old/lib/__pycache__/natools.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/natools.cpython-38.pyc (100%) create mode 100644 zen/jaklis.old/lib/__pycache__/profiles.cpython-36.pyc rename zen/{jaklis => jaklis.old}/lib/__pycache__/profiles.cpython-38.pyc (100%) create mode 100644 zen/jaklis.old/lib/cesium.py create mode 100644 zen/jaklis.old/lib/cesiumCommon.py create mode 100755 zen/jaklis.old/lib/crypt.py create mode 100644 zen/jaklis.old/lib/gva.py create mode 100644 zen/jaklis.old/lib/gvaBalance.py create mode 100644 zen/jaklis.old/lib/gvaHistory.py create mode 100644 zen/jaklis.old/lib/gvaPay.py create mode 100644 zen/jaklis.old/lib/likes.py create mode 100644 zen/jaklis.old/lib/messaging.py create mode 100755 zen/jaklis.old/lib/natools.py create mode 100644 zen/jaklis.old/lib/profiles.py create mode 100755 zen/jaklis.old/paiements.py create mode 100644 zen/jaklis.old/requirements.txt create mode 100755 zen/jaklis.old/setup.sh mode change 100644 => 100755 zen/jaklis/.env.template mode change 100644 => 100755 zen/jaklis/README.md create mode 100644 zen/jaklis/lib/__pycache__/offers.cpython-36.pyc mode change 100644 => 100755 zen/jaklis/lib/cesium.py mode change 100644 => 100755 zen/jaklis/lib/cesiumCommon.py mode change 100644 => 100755 zen/jaklis/lib/gva.py mode change 100644 => 100755 zen/jaklis/lib/gvaBalance.py mode change 100644 => 100755 zen/jaklis/lib/gvaHistory.py mode change 100644 => 100755 zen/jaklis/lib/gvaPay.py mode change 100644 => 100755 zen/jaklis/lib/likes.py mode change 100644 => 100755 zen/jaklis/lib/messaging.py create mode 100644 zen/jaklis/lib/offers.py mode change 100644 => 100755 zen/jaklis/lib/profiles.py mode change 100644 => 100755 zen/jaklis/requirements.txt diff --git a/ajouter_video.sh b/ajouter_video.sh index e625f0b..c6257a6 100755 --- a/ajouter_video.sh +++ b/ajouter_video.sh @@ -67,7 +67,34 @@ fi zenity --warning --width 300 --text "Ajoutez une vidéo à ASTROPORT/KODI" +# CHOOSE CATEGORY +CHOICE=$(zenity --entry --width 300 --title="Catégorie" --text="Choisissez la catégorie de votre vidéo" --entry-text="Film" Serie Anime Youtube) +[[ $CHOICE == "" ]] && exit 1 + +# LOWER CARACTERS +CAT=$(echo "${CHOICE}" | awk '{print tolower($0)}') +PREFIX=$(echo "${CAT}" | head -c 1 | awk '{ print toupper($0) }' ) # ex: F, S, A, Y +[[ $PREFIX == "" ]] && exit 1 + +case ${PREFIX} in ######################################################################## +# CASE ## YOUTUBE +######################################################################## + Y) + +[[ ! -d ~/astroport/youtube ]] && mkdir -p ~/astroport/youtube +YTURL=$(zenity --entry --width 300 --title "Lien ou identifiant à copier" --text "Copiez le lien (URL) ou l'ID de la vidéo" --entry-text="") +[[ $YTURL == "" ]] && exit 1 + +youtube-dl --no-mtime -o "~/astroport/youtube/%(id)s_%(title)s.%(ext)s" $YTURL + + ;; + +######################################################################## +# CASE ## DEFAULT +######################################################################## + *) + # SELECT FILE TO ADD TO ASTROPORT/KODI FILE=$(zenity --file-selection --title="Sélectionner le fichier à ajouter") echo "${FILE}" @@ -80,22 +107,13 @@ FILE_EXT="${FILE_NAME##*.}" FILE_TITLE="${FILE_NAME%.*}" # OPEN default browser and search TMDB -zenity --question --width 300 --text "IMPORTANT! Ouvrir le site themoviedb et récuperez son numéro d'identification" +zenity --question --width 300 --text "IMPORTANT! Nous allons ouvrir le site themoviedb pour y récuperer le numéro d'identification" [ $? == 1 ] && exit 1 xdg-open "https://www.themoviedb.org/search?query=${FILE_TITLE}" TMDB=$(zenity --entry --title="Identification TMDB" --text="Indiquez le numéro de la fiche du film. Exemple: https://www.themoviedb.org/movie/301528-toy-story-4 => 301528" --entry-text="") [[ $TMDB == "" ]] && exit 1 -# CHOOSE CATEGORY -CHOICE=$(zenity --entry --width 300 --title="Catégorie" --text="Choisissez la catégorie de la vidéo" --entry-text="Film" Serie Anime) -[[ $CHOICE == "" ]] && exit 1 - -# LOWER CARACTERS -CAT=$(echo "${CHOICE}" | awk '{print tolower($0)}') -PREFIX=$(echo "${CAT}" | head -c 1 | awk '{ print toupper($0) }' ) # ex: F, S, A -[[ $PREFIX == "" ]] && exit 1 - # VIDEO TITLE TITLE=$(zenity --entry --width 300 --title "Titre" --text "Indiquez le titre de la vidéo" --entry-text="${FILE_TITLE}") [[ $TITLE == "" ]] && exit 1 @@ -184,3 +202,5 @@ mv "${FILE_PATH}/${FILE_NAME}" ~/astroport/${CAT}/${TMDB}/ && zenity --warning - zenity --warning --width 300 --text "OK! Vidéo $FILE_NAME transférée dans Kodi/Vstream/Astroport" + ;; +esac diff --git a/install.sh b/install.sh index 5c73490..3ea9051 100755 --- a/install.sh +++ b/install.sh @@ -16,7 +16,7 @@ sudo apt-get install git fail2ban inotify-tools curl net-tools libsodium* python sudo apt-get install build-essential qrencode jq bc gawk ffmpeg sqlite dnsutils vlc -y [[ ! $(which kodi) ]] && sudo apt-get install kodi -y [[ "$USER" != "xbian" ]] && sudo apt-get install x11-utils zenity handbrake-gtk -y -# [[ ! $(which apache2) ]] && sudo apt-get install mariadb-server nginx ssl-cert php-imap php-cli php-curl php-sqlite3 php-gd php-json php-xml php-mbstring php-gettext php-mysql php-fpm -y +# [[ ! $(which apache2) ]] && sudo apt-get install mariadb-server nginx python3-certbot-nginx certbot ssl-cert php-imap php-cli php-curl php-sqlite3 php-gd php-json php-xml php-mbstring php-gettext php-mysql php-fpm -y pip3 install cryptography Ed25519 base58 google protobuf duniterpy diff --git a/zen/jaklis.old/.env b/zen/jaklis.old/.env new file mode 100644 index 0000000..ea6bd51 --- /dev/null +++ b/zen/jaklis.old/.env @@ -0,0 +1,5 @@ +DUNIKEY="/.zen/secret.dunikey" # Chemin du fichier de trousseau Ḡ1 de l'émetteur, au format PubSec +POD="https://data.gchange.fr" # Noeud Gchange utilisé pour l'envoi du message +#POD="https://g1.data.duniter.fr" # Noeud Cecium+ utilisé pour l'envoi du message +#POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours +#POD="https://g1.data.e-is.pro" diff --git a/zen/jaklis.old/.env.template b/zen/jaklis.old/.env.template new file mode 100644 index 0000000..dd81446 --- /dev/null +++ b/zen/jaklis.old/.env.template @@ -0,0 +1,6 @@ +DUNIKEY="" # Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec +#POD="https://g1.data.duniter.fr" # Adresse du pod Cesium ou Gchange à utiliser +POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours +#POD="https://data.gchange.fr" # Adresse du pod ḠChange à utiliser + +NODE="https://g1.librelois.fr/gva" diff --git a/zen/jaklis.old/README.md b/zen/jaklis.old/README.md new file mode 100644 index 0000000..59f1e61 --- /dev/null +++ b/zen/jaklis.old/README.md @@ -0,0 +1,76 @@ +# Client CLI for Cesium+/Ḡchange pod +## Installation + +Linux: +``` +bash setup.sh +``` + +Autre: +``` +Débrouillez-vous. +``` + +## Utilisation + +Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template). + +``` +./jaklis.py -h +``` + +``` +usage: jaklis.py [-h] [-v] [-k KEY] [-n NODE] {read,send,delete,get,set,erase,like,unlike,pay,history,balance} ... + +Client CLI pour Cesium+ et Ḡchange + +optional arguments: + -h, --help show this help message and exit + -v, --version Affiche la version actuelle du programme + -k KEY, --key KEY Chemin vers mon trousseau de clé (PubSec) + -n NODE, --node NODE Adresse du noeud Cesium+, Gchange ou Duniter à utiliser + +Commandes de jaklis: + {read,send,delete,get,set,erase,like,unlike,pay,history,balance} + read Lecture des messages + send Envoi d'un message + delete Supression d'un message + get Voir un profile Cesium+ + set Configurer son profile Cesium+ + erase Effacer son profile Cesium+ + like Voir les likes d'un profile / Liker un profile (option -s NOTE) + unlike Supprimer un like + pay Payer en Ḡ1 + history Voir l'historique des transactions d'un compte Ḡ1 + balance Voir le solde d'un compte Ḡ1 + +``` + +Utilisez `./jaklis CMD -h` où `CMD` est la commande souhaité pour obtenir l'aide détaillé de cette commande. + +### Exemples: + +Lire les 10 derniers messages de mon compte indiqué dans le fichier `.env` (par defaut 3 messages): +``` +./jaklis read -n10 +``` + +Envoyer un message à la clé publique `Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P` avec un fichier de trousseau particulier: +``` +./jaklis.py -k /home/saucisse/mon_fichier_de_trousseau.dunikey send -d Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P -t "Objet du message" -m "Corps de mon message" +``` + +Noter 4 étoiles le profile `S9EJbjbaGPnp26VuV6fKjR7raE1YkNhUGDgoydHvAJ1` sur gchange: +``` +./jaklis.py -n https://data.gchange.fr like -p S9EJbjbaGPnp26VuV6fKjR7raE1YkNhUGDgoydHvAJ1 -s 4 +``` + +Paramétrer mon profile Cesium+: +``` +./jaklis.py set -n "Sylvain Durif" -v "Bugarach" -a "42 route de Vénus" -d "Christ cosmique" -pos 48.539927 2.6608169 -s https://www.creationmonetaire.info -A mon_avatar.png +``` + +Effacer mon profile Gchange: +``` +./jaklis.py -n https://data.gchange.fr erase +``` diff --git a/zen/jaklis.old/jaklis.py b/zen/jaklis.old/jaklis.py new file mode 100755 index 0000000..0f40166 --- /dev/null +++ b/zen/jaklis.old/jaklis.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 + +import argparse, sys, os, getpass, string, random +from os.path import join, dirname +from shutil import copyfile +from dotenv import load_dotenv +from duniterpy.key import SigningKey + +__version__ = "0.0.2" + +MY_PATH = os.path.realpath(os.path.dirname(sys.argv[0])) + '/' + +# Get variables environment +if not os.path.isfile(MY_PATH + '.env'): + copyfile(MY_PATH + ".env.template",MY_PATH + ".env") +dotenv_path = join(dirname(__file__),MY_PATH + '.env') +load_dotenv(dotenv_path) + +# Parse arguments +parser = argparse.ArgumentParser(description="Client CLI pour Cesium+ et Ḡchange") +parser.add_argument('-v', '--version', action='store_true', help="Affiche la version actuelle du programme") +parser.add_argument('-k', '--key', help="Chemin vers mon trousseau de clé (PubSec)") +parser.add_argument('-n', '--node', help="Adresse du noeud Cesium+, Gchange ou Duniter à utiliser") + +subparsers = parser.add_subparsers(title="Commandes de jaklis", dest="cmd") +read_cmd = subparsers.add_parser('read', help="Lecture des messages") +send_cmd = subparsers.add_parser('send', help="Envoi d'un message") +delete_cmd = subparsers.add_parser('delete', help="Supression d'un message") +getProfile_cmd = subparsers.add_parser('get', help="Voir un profile Cesium+") +setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesium+") +eraseProfile_cmd = subparsers.add_parser('erase', help="Effacer son profile Cesium+") +like_cmd = subparsers.add_parser('like', help="Voir les likes d'un profile / Liker un profile (option -s NOTE)") +unlike_cmd = subparsers.add_parser('unlike', help="Supprimer un like") +pay_cmd = subparsers.add_parser('pay', help="Payer en Ḡ1") +history_cmd = subparsers.add_parser('history', help="Voir l'historique des transactions d'un compte Ḡ1") +balance_cmd = subparsers.add_parser('balance', help="Voir le solde d'un compte Ḡ1") + +# Messages management +read_cmd.add_argument('-n', '--number',type=int, default=3, help="Affiche les NUMBER derniers messages") +read_cmd.add_argument('-j', '--json', action='store_true', help="Sort au format JSON") +read_cmd.add_argument('-o', '--outbox', action='store_true', help="Lit les messages envoyés") + +send_cmd.add_argument('-d', '--destinataire', required=True, help="Destinataire du message") +send_cmd.add_argument('-t', '--titre', help="Titre du message à envoyer") +send_cmd.add_argument('-m', '--message', help="Message à envoyer") +send_cmd.add_argument('-f', '--fichier', help="Envoyer le message contenu dans le fichier 'FICHIER'") +send_cmd.add_argument('-o', '--outbox', action='store_true', help="Envoi le message sur la boite d'envoi") + +delete_cmd.add_argument('-i', '--id', action='append', nargs='+', required=True, help="ID(s) du/des message(s) à supprimer") +delete_cmd.add_argument('-o', '--outbox', action='store_true', help="Suppression d'un message envoyé") + +# Profiles management +setProfile_cmd.add_argument('-n', '--name', help="Nom du profile") +setProfile_cmd.add_argument('-d', '--description', help="Description du profile") +setProfile_cmd.add_argument('-v', '--ville', help="Ville du profile") +setProfile_cmd.add_argument('-a', '--adresse', help="Adresse du profile") +setProfile_cmd.add_argument('-pos', '--position', nargs=2, help="Points géographiques (lat + lon)") +setProfile_cmd.add_argument('-s', '--site', help="Site web du profile") +setProfile_cmd.add_argument('-A', '--avatar', help="Chemin vers mon avatar en PNG") + +getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile") +getProfile_cmd.add_argument('-a', '--avatar', action='store_true', help="Récupérer également l'avatar au format raw base64") + +# Likes management +like_cmd.add_argument('-p', '--profile', help="Profile cible") +like_cmd.add_argument('-s', '--stars', type=int, help="Nombre d'étoile") +unlike_cmd.add_argument('-p', '--profile', help="Profile à déliker") + +# GVA usage +pay_cmd.add_argument('-p', '--pubkey', help="Destinataire du paiement") +pay_cmd.add_argument('-a', '--amount', type=float, help="Montant de la transaction") +pay_cmd.add_argument('-c', '--comment', default="", help="Commentaire de la transaction") +pay_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool") +pay_cmd.add_argument('-v', '--verbose', action='store_true', help="Affiche le résultat JSON de la transaction") + +history_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé") +history_cmd.add_argument('-j', '--json', action='store_true', help="Affiche le résultat en format JSON") +history_cmd.add_argument('--nocolors', action='store_true', help="Affiche le résultat en noir et blanc") + +balance_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé") +balance_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool") + + +args = parser.parse_args() +cmd = args.cmd + +if args.version: + print(__version__) + sys.exit(0) + +if not cmd: + parser.print_help() + sys.exit(1) + +def createTmpDunikey(): + # Generate pseudo-random nonce + nonce=[] + for _ in range(32): + nonce.append(random.choice(string.ascii_letters + string.digits)) + nonce = ''.join(nonce) + keyPath = "/tmp/secret.dunikey-" + nonce + + key = SigningKey.from_credentials(getpass.getpass("Identifiant: "), getpass.getpass("Mot de passe: "), None) + key.save_pubsec_file(keyPath) + + return keyPath + +# Check if we need dunikey +try: + pubkey = args.pubkey +except: + pubkey = False +try: + profile = args.profile +except: + profile = False + +if cmd in ('history','balance','get') and (pubkey or profile): + noNeedDunikey = True + keyPath = False + try: + dunikey = args.pubkey + except: + dunikey = args.profile +else: + noNeedDunikey = False + if args.key: + dunikey = args.key + keyPath = False + else: + dunikey = os.getenv('DUNIKEY') + if not dunikey: + keyPath = createTmpDunikey() + dunikey = keyPath + else: + keyPath = False + if not os.path.isfile(dunikey): + HOME = os.getenv("HOME") + dunikey = HOME + dunikey + if not os.path.isfile(dunikey): + sys.stderr.write('Le fichier de trousseau {0} est introuvable.\n'.format(dunikey)) + sys.exit(1) + + +# Construct CesiumPlus object +if cmd in ("read","send","delete","set","get","erase","like","unlike"): + from lib.cesium import CesiumPlus + + if args.node: + pod = args.node + else: + pod = os.getenv('POD') + if not pod: + pod="https://g1.data.le-sou.org" + + cesium = CesiumPlus(dunikey, pod, noNeedDunikey) + + # Messaging + if cmd == "read": + cesium.read(args.number, args.outbox, args.json) + elif cmd == "send": + if args.fichier: + with open(args.fichier, 'r') as f: + msgT = f.read() + titre = msgT.splitlines(True)[0].replace('\n', '') + msg = ''.join(msgT.splitlines(True)[1:]) + if args.titre: + titre = args.titre + msg = msgT + elif args.titre and args.message: + titre = args.titre + msg = args.message + else: + titre = input("Indiquez le titre du message: ") + msg = input("Indiquez le contenu du message: ") + + cesium.send(titre, msg, args.destinataire, args.outbox) + + elif cmd == "delete": + cesium.delete(args.id[0], args.outbox) + + # Profiles + elif cmd == "set": + cesium.set(args.name, args.description, args.ville, args.adresse, args.position, args.site, args.avatar) + elif cmd == "get": + cesium.get(args.profile, args.avatar) + elif cmd == "erase": + cesium.erase() + + # Likes + elif cmd == "like": + if args.stars or args.stars == 0: + cesium.like(args.stars, args.profile) + else: + cesium.readLikes(args.profile) + elif cmd == "unlike": + cesium.unLike(args.profile) + +# Construct GVA object +elif cmd in ("pay","history","balance"): + from lib.gva import GvaApi + + if args.node: + node = args.node + else: + node = os.getenv('NODE') + if not node: + node="https://g1.librelois.fr/gva" + + if args.pubkey: + destPubkey = args.pubkey + else: + destPubkey = False + + gva = GvaApi(dunikey, node, destPubkey, noNeedDunikey) + + if cmd == "pay": + gva.pay(args.amount, args.comment, args.mempool, args.verbose) + if cmd == "history": + gva.history(args.json, args.nocolors) + if cmd == "balance": + gva.balance(args.mempool) + + +if keyPath: + os.remove(keyPath) diff --git a/zen/jaklis.old/lib/__pycache__/cesium.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/cesium.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08355fcf90d13f720aade39c9885b41a02edf266 GIT binary patch literal 3287 zcmZ`*OOG745pMRodwOOc-bjuhAc}1u9s|)LwzCcp#ZaPG;MfyEvSGP_WH8(0u6ni~ zBeKVl7CooYpUDr(h1Z;P%Q>ff#h#sqBKIzfMY4LDRrS?Je%9~D|GD+A@wI_v{oC4l zTI^4OUA5ffO-FFo)UuZvC%M) z^wT=v^9td3rJY&Lb)pcH`*k)iMb)IqRMpitLaE6);^oa`PLmQ5yqX*a+Et=BOa1Cp zexuH{U(cJ<`eo|p>Z_-J9i=f@cdn&@`8jo-#-u!~WjSg7X~{!u_J<@ zLrJ@mI$z%SzVpw=g52-iz<_^W(~3_!4%do zlKZq5ugRN2Xs5*db=u(pL>d!dx;V=Z$c&A3c$Qa_0>5Qa+llvR4N$505i~`=NV)PI z%GFJFXur>P@mhb z$nrxP8c%)WX&UET!TYas#MC&Ma6>997<)OV*!+rKBkXQD=y!&{#H$VZFX)j__dQ3QMV^p?1(vA|% z(3iv>8#mKro#LmWG5*RuSPXkzdfjSL3f<#EWip>NIhfb`F)xtuV8SIhK)gfPz2M{XMylCDEk^BVTq^4pBg1|MF*deZ)JYRk?QwSD$~zov5oD;Y06H{VswgL6i|u!6XY*6y zRa*+}f$6g6lJKmZLR4GHc(h7J8qFqB2_nrdFJYILVAdU8@;CJP7++q6I`vdm&xHKc z%y+>yZ-0h~x1;`B%#op@>a*A(DAH#)m~Xo_5hloTJO21c#>fy4#h&(U(0LomCjN*S zYZ-E2Eo}6NsKKKHJUZ*>0gs-LV4wd-Ce7A+VXR4 zxGo=Z^{3P47{WyIkV;%9z9h2Qc#uZfbCF%p zm2ZGYL!&d`~lXexBWDC~z(9L9S_`eq$h5ISGwO%gXq`~o7~$DQkTwsk9)R7!@=(mt(~ z=8}jDh`CgD*BDA*n$YTs!jxgmG@_{v@}_DGx=ey-hws|D4)1h9`I1=4#Q7H6XGdVJ GBl~|aXYQc@ literal 0 HcmV?d00001 diff --git a/zen/jaklis/lib/__pycache__/cesium.cpython-38.pyc b/zen/jaklis.old/lib/__pycache__/cesium.cpython-38.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/cesium.cpython-38.pyc rename to zen/jaklis.old/lib/__pycache__/cesium.cpython-38.pyc diff --git a/zen/jaklis.old/lib/__pycache__/cesiumCommon.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/cesiumCommon.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6fd2d221c4056edc528b1bd5f6a3661e50c1cc4 GIT binary patch literal 1725 zcmZuxOOG5i5VqaV>6r}yHjpSNY62)}#O{c&iG&m&kZd9z+C(J4rlY9U+xG7CY`wxfbhQb<38w{#7bA^nKW8z6krh^H$2P?^t8hv;c1thzN4rL;5- zgjzxDQ&m^S;ceL{;}mtuRb<5hY7;%ls~tEUI5OVChk8QfaftEnkH(X_6yt3v`1oNh zcU6;SVw{5K&8E3IQLXxnKs!&^;&goC|J5rsc1V3$0ovF(iCLLst_vyb&Dz*vs@=iyl z=*zE;=|M-iJ!exN=Q@}U^$1q|IU5o6qh9J|%AH+XK7Q)eeco$dK5t`o{90S(P`fZ! zp(q=@H@k4F5UCR3c3u>ro`|qX^(54FxL3C_MB1fVglKa9R=aguWKnQP;he^8+ASrr z!xfmc?*e?4hFS63lMwVp{-70MbxtS^{#2N@;Z|LR+qEoHy&6QvjMdcKELHV}5d3LO z#^a0NOz>@5v|?S#S^}q#Xi9S-fm;t{u7$D1qg?j^KwQ$nXqXhn0q5DojBb7N*_Z2g zldsortZy2t5LILY*+j7|Pr>}w_9RskGr(si)vXlKlEmPRE2^yK!VD(j5zlv^jK*o321Hm2BY7GI86sc`On**z vq56czLy%S>$8}w(7jt+PZSZyKJ^tV3#=pFX`D46!DgZbM{99%=3;cfoN~opo literal 0 HcmV?d00001 diff --git a/zen/jaklis/lib/__pycache__/cesiumCommon.cpython-38.pyc b/zen/jaklis.old/lib/__pycache__/cesiumCommon.cpython-38.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/cesiumCommon.cpython-38.pyc rename to zen/jaklis.old/lib/__pycache__/cesiumCommon.cpython-38.pyc diff --git a/zen/jaklis/lib/__pycache__/gva.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/gva.cpython-36.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/gva.cpython-36.pyc rename to zen/jaklis.old/lib/__pycache__/gva.cpython-36.pyc diff --git a/zen/jaklis/lib/__pycache__/gvaPay.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/gvaPay.cpython-36.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/gvaPay.cpython-36.pyc rename to zen/jaklis.old/lib/__pycache__/gvaPay.cpython-36.pyc diff --git a/zen/jaklis.old/lib/__pycache__/likes.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/likes.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad140ef85144a9fc5b66567a504270aa57e89257 GIT binary patch literal 6049 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 literal 0 HcmV?d00001 diff --git a/zen/jaklis/lib/__pycache__/likes.cpython-38.pyc b/zen/jaklis.old/lib/__pycache__/likes.cpython-38.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/likes.cpython-38.pyc rename to zen/jaklis.old/lib/__pycache__/likes.cpython-38.pyc diff --git a/zen/jaklis.old/lib/__pycache__/messaging.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/messaging.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..403471631f6b95b1973a6ca7fe816d5fa85026b8 GIT binary patch literal 6763 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 literal 0 HcmV?d00001 diff --git a/zen/jaklis/lib/__pycache__/natools.cpython-38.pyc b/zen/jaklis.old/lib/__pycache__/natools.cpython-38.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/natools.cpython-38.pyc rename to zen/jaklis.old/lib/__pycache__/natools.cpython-38.pyc diff --git a/zen/jaklis.old/lib/__pycache__/profiles.cpython-36.pyc b/zen/jaklis.old/lib/__pycache__/profiles.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f316106d4385c3b227250e6c0f3bc7991eec3de2 GIT binary patch literal 3044 zcmZ`*OLH7a5$@`)eoSj5&&rau-UvW!z2;nsdg$R}qUIc{c%h2j7&D6MO zdbX;^w#G9jIk@eW2qJvo%7x#-AK+3a2u`|j=ERrPlB|8u9hI4tU9Zmi@~isp>T3F* z?SJ(De8cnp?Ol5~>_;f(354(jE4{4IEMu@v%1HI`l9w)dn_v+-Uv9#<3Fe*D{?ezgDl z{;&5B_Ma5g_VNi}JVG%KAf!iY!g`qSUwK&1st85&ieW{uNZ<)YOQg_|SP^aLSa8g~ zW|POdI#H!GMS@}IX`|5Cdno1>^Ss)t*_n67mWlF~tp(HXEZ738-}M&0esAH!_F;4V zodt&-zz+1oMF5+_4)qE8rHfF6x4lIwBJzpY*$M0v?OM>0{>#9Fox+B^?TvheSrNyT zrcV7un_6D0uc997f7Zbw7Henzh@W}OxDH3rB0giIm}-i~yFu+pV~ozOC*HcS*WpiK z--Px?WNqNQjyQVLQsaKEJTgBpJx+aiu1;jZIakyfj(xTpj}fr&DQznJ%rbk8PAc%j;a* zq+e8%T2AVDOO5k^>`fR$Ordr>-fQaIVmb3Z6su)r?cay^3?K?!Em9Nji&}HSA)sVssaOZ zBg+%ZC;3?F80*k25(Fn3Oe>=aP`X3agdl!|Ke1fUudO(zsEXMb&cAy44jljeVQ*NC zW$#2w(R*I$r)HWLvX`4$KRH(Uq&Jt7-Y9=sD$^^~ac_D7;GOBIZ6i`AYJhbe$(mMR z9zl34^*Q5S^4Y>TYvY9%WsP<4a_BYG8h;8a@l$`kd6^_T*T>fphCVEaC+MMHUqx|w zQ^PEQRU%?r-%b+B!_13$iEzpWE~K$_1u( zfa9!P09}+9zroID?Xesm1D1Q$3|ORK=@eq{a^r6Y|C+!0up3xDGg4=K3Y^<;psU%m z8|iy!l(mLxFf37ko)cY-fjc#qHtw5hrVE*^s)!sY*rdL%gkHxqZqxM!Ssy^y5W$l#^!La?`+}g~BAcj0e}%+VH1#&B zUG74vDGvWXZfjTRvadleAEU9MIh%w`Sc(@Z)+E{17n98ODkwhm5ZFQ#mtcQ{L5H29 z|3o~V`ubhLM-(4MF*-xaP3}^J@Z|?0YQ7*Ue6m6Dg7=pqdE<3a<2baKXnh(i!$mj> zMT%A>EF@MPkCIUfM-ggUIEJ>Gqsc?ctsc(XdksT>i^zE10l${2aJcU|(hQ8O=Q}Sy zSoR3smz4EkL##i3`R!$|KbwLQ&u#bs)Y=m=jR@Pt0K7+#wl!EiA^Su$L3elCcn19BIP%Xnjjcmht-oAkR_A@19H*h&-!?MbS zX^1Oqh%3ORG?B+)jd+JAYLb_qk5g-!Ya@Sg^sB=e<}zy#9s6l_Xb zLnNR0$-C*DABHY^a5HM88AAU0*sl+IPU_S2tlzG@xnkT>med&?dXh QvL8~|4Nd(%e!Ua?4~41kO#lD@ literal 0 HcmV?d00001 diff --git a/zen/jaklis/lib/__pycache__/profiles.cpython-38.pyc b/zen/jaklis.old/lib/__pycache__/profiles.cpython-38.pyc similarity index 100% rename from zen/jaklis/lib/__pycache__/profiles.cpython-38.pyc rename to zen/jaklis.old/lib/__pycache__/profiles.cpython-38.pyc diff --git a/zen/jaklis.old/lib/cesium.py b/zen/jaklis.old/lib/cesium.py new file mode 100644 index 0000000..6a07fd9 --- /dev/null +++ b/zen/jaklis.old/lib/cesium.py @@ -0,0 +1,93 @@ +import re, string, random, base64 +from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX +from lib.messaging import ReadFromCesium, SendToCesium, DeleteFromCesium +from lib.profiles import Profiles +from lib.likes import ReadLikes, SendLikes, UnLikes + +class CesiumPlus(CesiumCommon): + + #################### Messaging #################### + + def read(self, nbrMsg, outbox, isJSON): + readCesium = ReadFromCesium(self.dunikey, self.pod) + jsonMsg = readCesium.sendDocument(nbrMsg, outbox) + if isJSON: + jsonFormat = readCesium.jsonMessages(jsonMsg, nbrMsg, outbox) + print(jsonFormat) + else: + readCesium.readMessages(jsonMsg, nbrMsg, outbox) + + def send(self, title, msg, recipient, outbox): + sendCesium = SendToCesium(self.dunikey, self.pod) + sendCesium.recipient = recipient + + # Generate pseudo-random nonce + nonce=[] + for _ in range(32): + nonce.append(random.choice(string.ascii_letters + string.digits)) + sendCesium.nonce = base64.b64decode(''.join(nonce)) + + finalDoc = sendCesium.configDoc(sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg)) # Configure JSON document to send + sendCesium.sendDocument(finalDoc, outbox) # Send final signed document + + def delete(self, idsMsgList, outbox): + deleteCesium = DeleteFromCesium(self.dunikey, self.pod) + # deleteCesium.issuer = recipient + for idMsg in idsMsgList: + finalDoc = deleteCesium.configDoc(idMsg, outbox) + deleteCesium.sendDocument(finalDoc, idMsg) + + #################### Profiles #################### + + def set(self, name=None, description=None, ville=None, adresse=None, position=None, site=None, avatar=None): + setProfile = Profiles(self.dunikey, self.pod) + document = setProfile.configDocSet(name, description, ville, adresse, position, site, avatar) + result = setProfile.sendDocument(document,'set') + + print(result) + return result + + def get(self, profile=None, avatar=None): + getProfile = Profiles(self.dunikey, self.pod, self.noNeedDunikey) + if not profile: + profile = self.pubkey + if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45: + scope = 'title' + else: + scope = '_id' + + document = getProfile.configDocGet(profile, scope, avatar) + resultJSON = getProfile.sendDocument(document, 'get') + result = getProfile.parseJSON(resultJSON) + + print(result) + + def erase(self): + eraseProfile = Profiles(self.dunikey, self.pod) + document = eraseProfile.configDocErase() + result = eraseProfile.sendDocument(document,'erase') + + print(result) + + #################### Likes #################### + + def readLikes(self, profile=False): + likes = ReadLikes(self.dunikey, self.pod, self.noNeedDunikey) + document = likes.configDoc(profile) + result = likes.sendDocument(document) + result = likes.parseResult(result) + + print(result) + + def like(self, stars, profile=False): + likes = SendLikes(self.dunikey, self.pod) + document = likes.configDoc(profile, stars) + if document: + likes.sendDocument(document, profile) + + def unLike(self, pubkey, silent=False): + likes = UnLikes(self.dunikey, self.pod) + idLike = likes.checkLike(pubkey) + if idLike: + document = likes.configDoc(idLike) + likes.sendDocument(document, silent) diff --git a/zen/jaklis.old/lib/cesiumCommon.py b/zen/jaklis.old/lib/cesiumCommon.py new file mode 100644 index 0000000..b68337d --- /dev/null +++ b/zen/jaklis.old/lib/cesiumCommon.py @@ -0,0 +1,51 @@ +import sys, re, json +from hashlib import sha256 +from lib.natools import fmt, sign, get_privkey + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +def pp_json(json_thing, sort=True, indents=4): + # Print beautifull JSON + if type(json_thing) is str: + print(json.dumps(json.loads(json_thing), sort_keys=sort, indent=indents)) + else: + print(json.dumps(json_thing, sort_keys=sort, indent=indents)) + return None + +class CesiumCommon: + def __init__(self, dunikey, pod, noNeedDunikey=False): + self.pod = pod + self.noNeedDunikey = noNeedDunikey + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + if noNeedDunikey: + self.pubkey = self.dunikey + else: + self.pubkey = get_privkey(dunikey, "pubsec").pubkey + + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + def signDoc(self, document): + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + + return json.dumps(finalJSON) diff --git a/zen/jaklis.old/lib/crypt.py b/zen/jaklis.old/lib/crypt.py new file mode 100755 index 0000000..ee4cfb2 --- /dev/null +++ b/zen/jaklis.old/lib/crypt.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +import base64, base58, sys, string, random +from natools import get_privkey, box_decrypt, box_encrypt, fmt + +def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str: + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +cmd = sys.argv[1] + +dunikey = getargv("-k", "private.dunikey") +msg = getargv("-m", "test") +pubkey = getargv("-p") + +def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(dunikey, "pubsec"), pubkey).decode() + +def encrypt(msg): + return fmt["64"](box_encrypt(msg.encode(), get_privkey(dunikey, "pubsec"), pubkey)).decode() + +if cmd == 'decrypt': + clear = decrypt(msg) + print(clear) +elif cmd == 'encrypt': + clear = encrypt(msg) + print(clear) + diff --git a/zen/jaklis.old/lib/gva.py b/zen/jaklis.old/lib/gva.py new file mode 100644 index 0000000..eaf22fd --- /dev/null +++ b/zen/jaklis.old/lib/gva.py @@ -0,0 +1,59 @@ +import sys, re +from lib.natools import get_privkey +from lib.gvaPay import Transaction, PUBKEY_REGEX +from lib.gvaHistory import History +from lib.gvaBalance import Balance + +class GvaApi(): + def __init__(self, dunikey, node, pubkey, noNeedDunikey=False): + self.noNeedDunikey = noNeedDunikey + self.dunikey = dunikey + self.node = node + if noNeedDunikey: + self.pubkey = self.dunikey + else: + self.pubkey = get_privkey(dunikey, "pubsec").pubkey + + if pubkey: + self.destPubkey = pubkey + else: + self.destPubkey = self.pubkey + + try: + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + raise ValueError("La clé publique n'est pas au bon format.") + except: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + raise + + try: + if not re.match(PUBKEY_REGEX, self.destPubkey) or len(self.destPubkey) > 45: + raise ValueError("La clé publique n'est pas au bon format.") + except: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + raise + + #################### Payments #################### + + def pay(self, amount, comment, mempool, verbose): + gva = Transaction(self.dunikey, self.node, self.destPubkey, amount, comment, mempool, verbose) + gva.genDoc() + gva.checkTXDoc() + gva.signDoc() + return gva.sendTXDoc() + + def history(self, isJSON=False, noColors=False): + gva = History(self.dunikey, self.node, self.destPubkey) + gva.sendDoc() + transList = gva.parseHistory() + + if isJSON: + transJson = gva.jsonHistory(transList) + print(transJson) + else: + gva.printHistory(transList, noColors) + + def balance(self, useMempool): + gva = Balance(self.dunikey, self.node, self.destPubkey, useMempool) + balanceValue = gva.sendDoc() + print(balanceValue) diff --git a/zen/jaklis.old/lib/gvaBalance.py b/zen/jaklis.old/lib/gvaBalance.py new file mode 100644 index 0000000..b148e7e --- /dev/null +++ b/zen/jaklis.old/lib/gvaBalance.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import sys, re, os.path, json, ast +from termcolor import colored +from lib.natools import fmt, sign, get_privkey +from gql import gql, Client +from gql.transport.aiohttp import AIOHTTPTransport + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +class Balance: + + def __init__(self, dunikey, node, pubkey, useMempool=False): + self.dunikey = dunikey + self.pubkey = pubkey if pubkey else get_privkey(dunikey, "pubsec").pubkey + self.useMempool = useMempool + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Define Duniter GVA node + transport = AIOHTTPTransport(url=node) + self.client = Client(transport=transport, fetch_schema_from_transport=True) + + def sendDoc(self): + # Build balance generation document + queryBuild = gql( + """ + query ($pubkey: String!){ + balance(script: $pubkey) { + amount + } + } + """ + ) + paramsBuild = { + "pubkey": self.pubkey + } + + # Send balance document + try: + balanceResult = self.client.execute(queryBuild, variable_values=paramsBuild) + except Exception as e: + message = ast.literal_eval(str(e))["message"] + sys.stderr.write("Echec de récupération du solde:\n" + message + "\n") + sys.exit(1) + + balanceValue = balanceResult['balance']['amount']/100 + # print(balanceValue) + return balanceValue diff --git a/zen/jaklis.old/lib/gvaHistory.py b/zen/jaklis.old/lib/gvaHistory.py new file mode 100644 index 0000000..a4f6400 --- /dev/null +++ b/zen/jaklis.old/lib/gvaHistory.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 + +import sys, re, os.path, json, ast, time +from datetime import datetime +from termcolor import colored +from lib.natools import fmt, sign, get_privkey +from gql import gql, Client +from gql.transport.aiohttp import AIOHTTPTransport + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +class History: + + def __init__(self, dunikey, node, pubkey): + self.dunikey = dunikey + self.pubkey = pubkey if pubkey else get_privkey(dunikey, "pubsec").pubkey + self.node = node + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Define Duniter GVA node + transport = AIOHTTPTransport(url=node) + self.client = Client(transport=transport, fetch_schema_from_transport=True) + + def sendDoc(self): + # Build history generation document + queryBuild = gql( + """ + query ($pubkey: String!){ + transactionsHistory(pubkey: $pubkey) { + received { + writtenTime + issuers + outputs + comment + } + sent { + writtenTime + issuers + outputs + comment + } + receiving { + issuers + outputs + comment + } + sending { + issuers + outputs + comment + } + } + balance(script: $pubkey) { + amount + base + } + node { + peer { + currency + } + } + currentUd { + amount + base + } + } + """ + ) + paramsBuild = { + "pubkey": self.pubkey + } + + # Send history document + try: + self.historyDoc = self.client.execute(queryBuild, variable_values=paramsBuild) + except Exception as e: + message = ast.literal_eval(str(e))["message"] + sys.stderr.write("Echec de récupération de l'historique:\n" + message + "\n") + sys.exit(1) + + + def parseHistory(self): + trans = [] + i = 0 + + currentBase = int(self.historyDoc['currentUd']['base']) + self.UD = self.historyDoc['currentUd']['amount']/100 + + for sens in 'received','sent','receiving','sending': + res = self.historyDoc['transactionsHistory'][sens] + for bloc in res: + output = bloc['outputs'][0] + outPubkey = output.split("SIG(")[1].replace(')','') + if sens in ('received','receiving') or self.pubkey != outPubkey: + trans.append(i) + trans[i] = [] + trans[i].append(sens) + if sens in ('receiving','sending'): + trans[i].append(int(time.time())) + else: + trans[i].append(bloc['writtenTime']) + if sens in ('sent','sending'): + trans[i].append(outPubkey) + amount = int('-' + output.split(':')[0]) + else: + trans[i].append(bloc['issuers'][0]) + amount = int(output.split(':')[0]) + base = int(output.split(':')[1]) + applyBase = base-currentBase + amount = round(amount*pow(10,applyBase)/100, 2) + # if referential == 'DU': amount = round(amount/UD, 2) + trans[i].append(amount) + trans[i].append(round(amount/self.UD, 2)) + trans[i].append(bloc['comment']) + trans[i].append(base) + i += 1 + + # Order transactions by date + trans.sort(key=lambda x: x[1]) + + # Keep only base if there is base change + lastBase = 0 + for i in trans: + if i[6] == lastBase: i[6] = None + else: lastBase = i[6] + + return trans + + def printHistory(self, trans, noColors): + # Get balance + balance = self.historyDoc['balance']['amount']/100 + balanceUD = round(balance/self.UD, 2) + + # Get currency + currency = self.historyDoc['node']['peer']['currency'] + if currency == 'g1': currency = 'Ḡ1' + elif currency == 'g1-test': currency = 'GT' + # if referential == 'DU': currency = 'DU/' + currency.lower() + + # Get terminal size + rows = int(os.popen('stty size', 'r').read().split()[1]) + + # Display history + print('+', end='') + print('-'.center(rows-1, '-')) + if noColors: isBold = isBoldEnd = '' + else: + isBold = '\033[1m' + isBoldEnd = '\033[0m' + print(isBold + "|{: <19} | {: <12} | {: <7} | {: <7} | {: <30}".format(" Date"," De / À"," {0}".format(currency)," DU/{0}".format(currency.lower()),"Commentaire") + isBoldEnd) + print('|', end='') + for t in trans: + if t[0] == "received": color = "green" + elif t[0] == "receiving": color = "yellow" + elif t[0] == "sending": color = "red" + else: color = "blue" + if noColors: + color = None + if t[0] in ('receiving','sending'): + comment = '(EN ATTENTE) ' + t[5] + else: + comment = t[5] + else: + comment = t[5] + + date = datetime.fromtimestamp(t[1]).strftime("%d/%m/%Y à %H:%M") + print('-'.center(rows-1, '-')) + if t[6]: + print('|', end='') + print(' Changement de base : {0} '.format(t[6]).center(rows-1, '#')) + print('|', end='') + print('-'.center(rows-1, '-')) + print('|', end='') + printKey = t[2][0:8] + '\u2026' + t[2][-3:] + if noColors: + print(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment)) + else: + print(colored(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment), color)) + print('|', end='') + print('-'.center(rows-1, '-')) + print('|', end='') + print(isBold + 'Solde du compte: {0} {1} ({2} DU/{3})'.format(balance, currency, balanceUD, currency.lower()).center(rows-1, ' ') + isBoldEnd) + print('+', end='') + print(''.center(rows-1, '-')) + if not noColors: + print(colored('Reçus', 'green'), '-', colored('En cours de réception', 'yellow'), '-', colored('Envoyé', 'blue'), '-', colored("En cours d'envoi", 'red')) + + return trans + + def jsonHistory(self, transList): + dailyJSON = [] + for i, trans in enumerate(transList): + dailyJSON.append(i) + dailyJSON[i] = {} + dailyJSON[i]['date'] = trans[1] + dailyJSON[i]['pubkey'] = trans[2] + dailyJSON[i]['amount'] = trans[3] + dailyJSON[i]['amountUD'] = trans[4] + dailyJSON[i]['comment'] = trans[5] + + dailyJSON = json.dumps(dailyJSON, indent=2) + # If we want to write JSON to a file + #jsonFile = open("history-{0}.json".format(self.pubkey[0:8]), "w") + #jsonFile.writelines(dailyJSON + '\n') + #jsonFile.close() + return dailyJSON + diff --git a/zen/jaklis.old/lib/gvaPay.py b/zen/jaklis.old/lib/gvaPay.py new file mode 100644 index 0000000..e017c88 --- /dev/null +++ b/zen/jaklis.old/lib/gvaPay.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + +import sys, re, os.path, json, ast +from termcolor import colored +from lib.natools import fmt, sign, get_privkey +from gql import gql, Client +from gql.transport.aiohttp import AIOHTTPTransport + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +class Transaction: + + def __init__(self, dunikey, node, recipient, amount, comment='', useMempool=False, verbose=False): + self.dunikey = dunikey + self.recipient = recipient + self.amount = int(amount*100) + self.comment = comment + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.useMempool = useMempool + self.verbose = verbose + self.node = node + self._isChange = False + + try: + if not re.match(PUBKEY_REGEX, recipient) or len(recipient) > 45: + raise ValueError("La clé publique n'est pas au bon format.") + except: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + raise + + + try: + if recipient == self.issuer: + raise ValueError('Le destinataire ne peut pas être vous même.') + except: + sys.stderr.write("Le destinataire ne peut pas être vous même.\n") + raise + + + # Define Duniter GVA node + transport = AIOHTTPTransport(url=node) + self.client = Client(transport=transport, fetch_schema_from_transport=True) + + def genDoc(self): + # Build TX generation document + if self.verbose: print("useMempool:", str(self.useMempool)) + queryBuild = gql( + """ + query ($recipient: String!, $issuer: String!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx( + amount: $amount + comment: $comment + issuer: $issuer + recipient: $recipient + useMempoolSources: $useMempool + ) + } + """ + ) + paramsBuild = { + "recipient": self.recipient, + "issuer": self.issuer, + "amount": int(self.amount), + "comment": self.comment, + "useMempool": self.useMempool + } + + # Send TX document + try: + # self.txDoc = [] + self.txDoc = self.client.execute(queryBuild, variable_values=paramsBuild)['genTx'] + if self.verbose: print(self.txDoc[0]) + return self.txDoc + except Exception as e: + message = ast.literal_eval(str(e))["message"] + sys.stderr.write("Echec de la génération du document:\n" + message + "\n") + raise + + + # Check document + def checkTXDoc(self): + issuerRaw=[];outAmount=[];outPubkey=[];commentRaw=[] + for docs in self.txDoc: + docList = docs.splitlines() + for i, line in enumerate(docList): + if re.search("Issuers:", line): + issuerRaw.append(docList[(i + 1) % len(docList)]) + if re.search("Outputs:", line): + outputRaw = docList[(i + 1) % len(docList)].split(":") + outAmount.append(int(outputRaw[0])) + outPubkey.append(outputRaw[2].split("SIG(")[1].replace(')','')) + if re.search("Comment:", line): + commentRaw.append(line.split(': ', 1)[1]) + + # Check if it's only a change transaction + if all(i == self.issuer for i in outPubkey): + print("Le document contient une transaction de change") + self.isChange = True + # Check validity of the document + elif all(i != self.issuer for i in issuerRaw) or sum(outAmount) != self.amount or all(i != self.recipient for i in outPubkey) or all(i != self.comment for i in commentRaw): + sys.stderr.write(colored("Le document généré est corrompu !\nLe noeud " + self.node + "a peut être un dysfonctionnement.\n", 'red')) + sys.stderr.write(colored(issuerRaw[0] + " envoi " + str(outAmount[0]) + " vers " + outPubkey[0] + " with comment: " + commentRaw[0] + "\n", "yellow")) + raise ValueError('Le document généré est corrompu !') + else: + print("Le document généré est conforme.") + self.isChange = False + return self.txDoc + + def signDoc(self): + # Sign TX documents + signature=[] + self.signedDoc=[] + for i, docs in enumerate(self.txDoc): + signature.append(fmt["64"](sign(docs.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(docs.encode())])) + self.signedDoc.append(docs + signature[i].decode()) + return self.signedDoc + + + def sendTXDoc(self): + # Build TX documents + txResult=[] + for docs in self.signedDoc: + querySign = gql( + """ + mutation ($signedDoc: String!){ tx( + rawTx: $signedDoc + ) { + version + issuers + outputs + } + } + """ + ) + paramsSign = { + "signedDoc": docs + } + + # Send TX Signed document + try: + txResult.append(str(self.client.execute(querySign, variable_values=paramsSign))) + except Exception as e: + message = ast.literal_eval(str(e))["message"] + sys.stderr.write("Echec de la transaction:\n" + message + "\n") + if self.verbose: + sys.stderr.write("Document final:\n" + docs) + raise ValueError(message) + else: + if self.isChange: + self.send() + else: + print(colored("Transaction effectué avec succès !", "green")) + if self.verbose: + print(docs) + break + + return txResult + + def _getIsChange(self): + return self._isChange + def _setIsChange(self, newChange): + if self.verbose: print("_setIsChange: ", str(newChange)) + self._isChange = newChange + if newChange: self.useMempool == True + isChange = property(_getIsChange, _setIsChange) + + def send(self): + result = self.genDoc() + result = self.checkTXDoc() + result = self.signDoc() + result = self.sendTXDoc() + return result + diff --git a/zen/jaklis.old/lib/likes.py b/zen/jaklis.old/lib/likes.py new file mode 100644 index 0000000..5eee339 --- /dev/null +++ b/zen/jaklis.old/lib/likes.py @@ -0,0 +1,242 @@ +import os, sys, ast, requests, json, base58, base64, time, string, random, re +from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from time import sleep +from hashlib import sha256 +from datetime import datetime +from termcolor import colored +from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX + +class ReadLikes(CesiumCommon): + # Configure JSON document to send + def configDoc(self, profile): + if not profile: profile = self.pubkey + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'index': 'user'}}, + {'term': {'type': 'profile'}}, + {'term': {'id': profile}}, + {'term': {'kind': 'STAR'}} + ] + # data['query']['bool']['should'] = {'term':{'issuer': self.issuer}} + data['size'] = 5000 + data['_source'] = ['issuer','level'] + data['aggs'] = { + 'level_sum': { + 'sum': { + 'field': 'level' + } + } + } + + return json.dumps(data) + + def sendDocument(self, document): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/like/record/_search'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') + + def parseResult(self, result): + result = json.loads(result) + totalLikes = result['hits']['total'] + totalValue = result['aggregations']['level_sum']['value'] + if totalLikes: + score = totalValue/totalLikes + else: + score = 0 + raw = result['hits']['hits'] + finalPrint = {} + finalPrint['likes'] = [] + for i in raw: + issuer = i['_source']['issuer'] + # print(issuer) + gProfile = self.getProfile(issuer) + try: + pseudo = gProfile['title'] + except: + pseudo = '' + try: + payTo = gProfile['pubkey'] + except: + payTo = '' + id = i['_id'] + level = i['_source']['level'] + if issuer == self.pubkey: + finalPrint['yours'] = { 'id' : id, 'pseudo' : pseudo, 'payTo' : payTo, 'level' : level } + else: + finalPrint['likes'].append({ 'issuer' : issuer, 'pseudo' : pseudo, 'payTo' : payTo, 'level' : level }) + finalPrint['score'] = score + + return json.dumps(finalPrint) + + def getProfile(self, profile): + headers = { + 'Content-type': 'application/json', + } + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'_index': 'user'}}, + {'term': {'_type': 'profile'}}, + {'term': {'_id': profile}} + ] + data['_source'] = ['title','pubkey'] + + data = json.dumps(data) + + result = requests.post('{0}/user/profile/_search'.format(self.pod), headers=headers, data=data) + result = json.loads(result.text)['hits']['hits'] + for i in result: + return i['_source'] + + +#################### Like class #################### + + +class SendLikes(CesiumCommon): + # Configure JSON document to send + def configDoc(self, profile, likes): + if not profile: profile = self.pubkey + if likes not in range(0, 6): + sys.stderr.write(colored('Votre like doit être compris entre 0 et 5.\n', 'red')) + return False + + + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "user" + data['type'] = "profile" + data['id'] = profile + data['kind'] = "STAR" + data['level'] = likes + data['time'] = timeSent + data['issuer'] = self.pubkey + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, pubkey): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/user/profile/:id/_like'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + print(colored("Profile liké avec succès !", 'green')) + return result.text + elif result.status_code == 400: + resultJson = json.loads(result.text) + if 'DuplicatedDocumentException' in resultJson['error']: + rmLike = UnLikes(self.dunikey, self.pod) + idLike = rmLike.checkLike(pubkey) + if idLike: + document = rmLike.configDoc(idLike) + rmLike.sendDocument(document, True) + sleep(0.5) + self.sendDocument(document, pubkey) + return resultJson['error'] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + else: + resultJson = json.loads(result.text) + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + + +#################### Unlike class #################### + + +class UnLikes(CesiumCommon): + # Check if you liked this profile + def checkLike(self, pubkey): + readProfileLikes = ReadLikes(self.dunikey, self.pod) + document = readProfileLikes.configDoc(pubkey) + result = readProfileLikes.sendDocument(document) + result = readProfileLikes.parseResult(result) + result = json.loads(result) + + if 'yours' in result: + myLike = result['yours']['id'] + return myLike + else: + sys.stderr.write("Vous n'avez pas liké ce profile\n") + return False + + # Configure JSON document to send + def configDoc(self, idLike): + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "like" + data['type'] = "record" + data['id'] = idLike + data['issuer'] = self.pubkey + data['time'] = timeSent + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, silent): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + if not silent: + print(colored("Like supprimé avec succès !", 'green')) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') diff --git a/zen/jaklis.old/lib/messaging.py b/zen/jaklis.old/lib/messaging.py new file mode 100644 index 0000000..2165182 --- /dev/null +++ b/zen/jaklis.old/lib/messaging.py @@ -0,0 +1,236 @@ +import os, sys, ast, requests, json, base58, base64 +from time import time +from datetime import datetime +from termcolor import colored +from lib.natools import fmt, get_privkey, box_decrypt, box_encrypt +from lib.cesiumCommon import CesiumCommon, pp_json, PUBKEY_REGEX + + +#################### Reading class #################### + + +class ReadFromCesium(CesiumCommon): + # Configure JSON document to send + def configDoc(self, nbrMsg, outbox): + boxType = "issuer" if outbox else "recipient" + + data = {} + data['sort'] = { "time": "desc" } + data['from'] = 0 + data['size'] = nbrMsg + data['_source'] = ['issuer','recipient','title','content','time','nonce','read_signature'] + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = {} + data['query']['bool']['filter']['term'] = {} + data['query']['bool']['filter']['term'][boxType] = self.pubkey + + document = json.dumps(data) + return document + + def sendDocument(self, nbrMsg, outbox): + boxType = "outbox" if outbox else "inbox" + + document = self.configDoc(nbrMsg, outbox) + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/message/{1}/_search'.format(self.pod, boxType), headers=headers, data=document) + if result.status_code == 200: + return result.json()["hits"] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text) + + # Parse JSON result and display messages + def readMessages(self, msgJSON, nbrMsg, outbox): + def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() + + # Get terminal size + rows = int(os.popen('stty size', 'r').read().split()[1]) + + totalMsg = msgJSON["total"] + if nbrMsg > totalMsg: + nbrMsg = totalMsg + + if totalMsg == 0: + print(colored("Aucun message à afficher.", 'yellow')) + return True + else: + infoTotal = " Nombre de messages: " + str(nbrMsg) + "/" + str(totalMsg) + " " + print(colored(infoTotal.center(rows, '#'), "yellow")) + for hits in msgJSON["hits"]: + self.idMsg = hits["_id"] + msgSrc = hits["_source"] + self.issuer = msgSrc["issuer"] + nonce = msgSrc["nonce"] + nonce = base58.b58decode(nonce) + self.dateS = msgSrc["time"] + date = datetime.fromtimestamp(self.dateS).strftime(", le %d/%m/%Y à %H:%M ") + if outbox: + startHeader = " À " + msgSrc["recipient"] + else: + startHeader = " De " + self.issuer + headerMsg = startHeader + date + "(ID: {})".format(self.idMsg) + " " + + print('-'.center(rows, '-')) + print(colored(headerMsg, "blue").center(rows+9, '-')) + print('-'.center(rows, '-')) + try: + self.title = decrypt(msgSrc["title"]) + self.content = decrypt(msgSrc["content"]) + except Exception as e: + sys.stderr.write(colored(str(e), 'red') + '\n') + pp_json(hits) + continue + print('\033[1m' + self.title + '\033[0m') + print(self.content) + + print(colored(infoTotal.center(rows, '#'), "yellow")) + + # Parse JSON result and display messages + def jsonMessages(self, msgJSON, nbrMsg, outbox): + def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() + + totalMsg = msgJSON["total"] + if nbrMsg > totalMsg: + nbrMsg = totalMsg + + if totalMsg == 0: + print("Aucun message à afficher") + return True + else: + data = [] + # data.append({}) + # data[0]['total'] = totalMsg + for i, hits in enumerate(msgJSON["hits"]): + self.idMsg = hits["_id"] + msgSrc = hits["_source"] + self.issuer = msgSrc["issuer"] + nonce = msgSrc["nonce"] + nonce = base58.b58decode(nonce) + self.date = msgSrc["time"] + + if outbox: + pubkey = msgSrc["recipient"] + else: + pubkey = self.issuer + + try: + self.title = decrypt(msgSrc["title"]) + self.content = decrypt(msgSrc["content"]) + except Exception as e: + sys.stderr.write(colored(str(e), 'red') + '\n') + pp_json(hits) + continue + + data.append(i) + data[i] = {} + data[i]['id'] = self.idMsg + data[i]['date'] = self.date + data[i]['pubkey'] = pubkey + data[i]['title'] = self.title + data[i]['content'] = self.content + + data = json.dumps(data, indent=2) + return data + + +#################### Sending class #################### + + +class SendToCesium(CesiumCommon): + def encryptMsg(self, msg): + return fmt["64"](box_encrypt(msg.encode(), get_privkey(self.dunikey, "pubsec"), self.recipient, self.nonce)).decode() + + def configDoc(self, title, msg): + b58nonce = base58.b58encode(self.nonce).decode() + + # Get current timestamp + timeSent = int(time()) + + # Generate custom JSON + data = {} + data['issuer'] = self.pubkey + data['recipient'] = self.recipient + data['title'] = title + data['content'] = msg + data['time'] = timeSent + data['nonce'] = b58nonce + data['version'] = 2 + document = json.dumps(data) + + return self.signDoc(document) + + + def sendDocument(self, document, outbox): + boxType = "outbox" if outbox else "inbox" + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/message/{1}?pubkey={2}'.format(self.pod, boxType, self.recipient), headers=headers, data=document) + except Exception as e: + sys.stderr.write("Impossible d'envoyer le message:\n" + str(e)) + sys.exit(1) + else: + if result.status_code == 200: + print(colored("Message envoyé avec succès !", "green")) + print("ID: " + result.text) + return result + else: + sys.stderr.write("Erreur inconnue:" + '\n') + print(str(pp_json(result.text)) + '\n') + + +#################### Deleting class #################### + + +class DeleteFromCesium(CesiumCommon): + def configDoc(self, idMsg, outbox): + # Get current timestamp + timeSent = int(time()) + + boxType = "outbox" if outbox else "inbox" + + # Generate document to customize + data = {} + data['version'] = 2 + data['index'] = "message" + data['type'] = boxType + data['id'] = idMsg + data['issuer'] = self.pubkey + data['time'] = timeSent + document = json.dumps(data) + + return self.signDoc(document) + + def sendDocument(self, document, idMsg): + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + if result.status_code == 404: + raise ValueError("Message introuvable") + elif result.status_code == 403: + raise ValueError("Vous n'êtes pas l'auteur de ce message.") + except Exception as e: + sys.stderr.write(colored("Impossible de supprimer le message {0}:\n".format(idMsg), 'red') + str(e) + "\n") + return False + else: + if result.status_code == 200: + print(colored("Message {0} supprimé avec succès !".format(idMsg), "green")) + return result + else: + sys.stderr.write("Erreur inconnue.") diff --git a/zen/jaklis.old/lib/natools.py b/zen/jaklis.old/lib/natools.py new file mode 100755 index 0000000..18f06d1 --- /dev/null +++ b/zen/jaklis.old/lib/natools.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +""" + CopyLeft 2020 Pascal Engélibert + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +""" + +__version__ = "1.3.1" + +import os, sys, duniterpy.key, libnacl, base58, base64, getpass + +def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str: + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +def read_data(data_path, b=True): + if data_path == "-": + if b: + return sys.stdin.buffer.read() + else: + return sys.stdin.read() + else: + return open(os.path.expanduser(data_path), "rb" if b else "r").read() + +def write_data(data, result_path): + if result_path == "-": + os.fdopen(sys.stdout.fileno(), 'wb').write(data) + else: + open(os.path.expanduser(result_path), "wb").write(data) + +def encrypt(data, pubkey): + return duniterpy.key.PublicKey(pubkey).encrypt_seal(data) + +def decrypt(data, privkey): + return privkey.decrypt_seal(data) + +def box_encrypt(data, privkey, pubkey, nonce=None, attach_nonce=False): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + data = box.encrypt(data, nonce) if nonce else box.encrypt(data) + return data if attach_nonce else data[24:] + +def box_decrypt(data, privkey, pubkey, nonce=None): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + return box.decrypt(data, nonce) if nonce else box.decrypt(data) + +def sign(data, privkey): + return privkey.sign(data) + +def verify(data, pubkey): + try: + ret = libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data) + sys.stderr.write("Signature OK!\n") + return ret + except ValueError: + sys.stderr.write("Bad signature!\n") + exit(1) + +def get_privkey(privkey_path, privkey_format): + if privkey_format == "pubsec": + if privkey_path == "*": + privkey_path = "privkey.pubsec" + return duniterpy.key.SigningKey.from_pubsec_file(privkey_path) + + elif privkey_format == "cred": + if privkey_path == "*": + privkey_path = "-" + if privkey_path == "-": + return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Password: "), getpass.getpass("Salt: ")) + else: + return duniterpy.key.SigningKey.from_credentials_file(privkey_path) + + elif privkey_format == "seedh": + if privkey_path == "*": + privkey_path = "authfile.seedhex" + return duniterpy.key.SigningKey.from_seedhex(read_data(privkey_path, False)) + + elif privkey_format == "wif": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_file(privkey_path) + + elif privkey_format == "wifh": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_hex(privkey_path) + + elif privkey_format == "ssb": + if privkey_path == "*": + privkey_path = "secret" + return duniterpy.key.SigningKey.from_ssb_file(privkey_path) + + elif privkey_format == "key": + if privkey_path == "*": + privkey_path = "authfile.key" + return duniterpy.key.SigningKey.from_private_key(privkey_path) + + print("Error: unknown privkey format") + +def fill_pubkey(pubkey, length=32): + while pubkey[0] == 0: + pubkey = pubkey[1:] + return b"\x00"*(length-len(pubkey)) + pubkey + +def pubkey_checksum(pubkey, length=32, clength=3): + return base58.b58encode(libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(fill_pubkey(base58.b58decode(pubkey), length)))).decode()[:clength] + +# returns (pubkey:bytes|None, deprecated_length:bool) +def check_pubkey(pubkey): + if ":" in pubkey: + parts = pubkey.split(":") + if len(parts[1]) < 3 or len(parts[1]) > 32: + return (None, False) + for i in range(32, 0, -1): + if pubkey_checksum(parts[0], i, len(parts[1])) == parts[1]: + return (parts[0], i < 32) + return (None, False) + return (pubkey, False) + +fmt = { + "raw": lambda data: data, + "16": lambda data: data.hex().encode(), + "32": lambda data: base64.b32encode(data), + "58": lambda data: base58.b58encode(data), + "64": lambda data: base64.b64encode(data), + "64u": lambda data: base64.urlsafe_b64encode(data), + "85": lambda data: base64.b85encode(data), +} + +defmt = { + "raw": lambda data: data, + "16": lambda data: bytes.fromhex(data), + "32": lambda data: base64.b32decode(data), + "58": lambda data: base58.b58decode(data), + "64": lambda data: base64.b64decode(data), + "85": lambda data: base64.b85decode(data), +} + +def show_help(): + print("""Usage: +python3 natools.py [options] + +Commands: + encrypt Encrypt data + decrypt Decrypt data + box-encrypt Encrypt data (NaCl box) + box-decrypt Decrypt data (NaCl box) + sign Sign data + verify Verify data + pubkey Display pubkey + pk Display b58 pubkey shorthand + +Options: + -c Display pubkey checksum + -f Private key format (default: cred) + key cred pubsec seedh ssb wif wifh + -i Input file path (default: -) + -I Input format: raw 16 32 58 64 85 (default: raw) + -k Privkey file path (* for auto) (default: *) + -n Nonce (b64, 24 bytes) (for NaCl box) + -N Attach nonce to output (for NaCl box encryption) + --noinc Do not include msg after signature + -o Output file path (default: -) + -O Output format: raw 16 32 58 64 64u 85 (default: raw) + -p Pubkey (base58) + + --help Show help + --version Show version + --debug Debug mode (display full errors) + +Note: "-" means stdin or stdout. +""") + +if __name__ == "__main__": + + if "--help" in sys.argv: + show_help() + exit() + + if "--version" in sys.argv: + print(__version__) + exit() + + privkey_format = getargv("-f", "cred") + data_path = getargv("-i", "-") + privkey_path = getargv("-k", "*") + pubkey = getargv("-p") + result_path = getargv("-o", "-") + output_format = getargv("-O", "raw") + input_format = getargv("-I", "raw") + + if pubkey: + pubkey, len_deprecated = check_pubkey(pubkey) + if not pubkey: + print("Invalid pubkey checksum! Please check spelling.") + exit(1) + if len(base58.b58decode(pubkey)) > 32: + print("Invalid pubkey: too long!") + exit(1) + if len_deprecated: + print("Warning: valid pubkey checksum, but deprecated format (truncating zeros)") + + try: + if sys.argv[1] == "encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](encrypt(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "decrypt": + write_data(fmt[output_format](decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format))), result_path) + + elif sys.argv[1] == "box-encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + attach_nonce = "-N" in sys.argv + write_data(fmt[output_format](box_encrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce, attach_nonce)), result_path) + + elif sys.argv[1] == "box-decrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + write_data(fmt[output_format](box_decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce)), result_path) + + elif sys.argv[1] == "sign": + data = defmt[input_format](read_data(data_path)) + signed = sign(data, get_privkey(privkey_path, privkey_format)) + + if "--noinc" in sys.argv: + signed = signed[:len(signed)-len(data)] + + write_data(fmt[output_format](signed), result_path) + + elif sys.argv[1] == "verify": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](verify(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "pubkey": + if pubkey: + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + else: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + + elif sys.argv[1] == "pk": + if not pubkey: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv: + print("{}:{}".format(pubkey, pubkey_checksum(pubkey))) + else: + print(pubkey) + + else: + show_help() + + except Exception as e: + if "--debug" in sys.argv: + 0/0 # DEBUG MODE (raise error when handling error to display backtrace) + sys.stderr.write("Error: {}\n".format(e)) + show_help() + exit(1) diff --git a/zen/jaklis.old/lib/profiles.py b/zen/jaklis.old/lib/profiles.py new file mode 100644 index 0000000..5386c89 --- /dev/null +++ b/zen/jaklis.old/lib/profiles.py @@ -0,0 +1,125 @@ +import sys, re, json, requests, base64 +from time import time +from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX + + +class Profiles(CesiumCommon): + # Configure JSON document SET to send + def configDocSet(self, name, description, city, address, pos, socials, avatar): + timeSent = int(time()) + + data = {} + if name: data['title'] = name + if description: data['description'] = description + if address: data['address'] = address + if city: data['city'] = city + if pos: + geoPoint = {} + geoPoint['lat'] = pos[0] + geoPoint['lon'] = pos[1] + data['geoPoint'] = geoPoint + if socials: + data['socials'] = [] + data['socials'].append({}) + data['socials'][0]['type'] = "web" + data['socials'][0]['url'] = socials + if avatar: + avatar = open(avatar, 'rb').read() + avatar = base64.b64encode(avatar).decode() + data['avatar'] = {} + data['avatar']['_content'] = avatar + data['avatar']['_content_type'] = "image/png" + data['time'] = timeSent + data['issuer'] = self.pubkey + data['version'] = 2 + data['tags'] = [] + + document = json.dumps(data) + + return self.signDoc(document) + + # Configure JSON document GET to send + def configDocGet(self, profile, scope='title', getAvatar=None): + + if getAvatar: + avatar = "avatar" + else: + avatar = "avatar._content_type" + + data = { + "query": { + "bool": { + "should":[ + { + "match":{ + scope:{ + "query": profile,"boost":2 + } + } + },{ + "prefix": {scope: profile} + } + ] + } + },"highlight": { + "fields": { + "title":{}, + "tags":{} + } + },"from":0, + "size":100, + "_source":["title", avatar,"description","city","address","socials.url","creationTime","membersCount","type"], + "indices_boost":{"user":100,"page":1,"group":0.01 + } + } + + document = json.dumps(data) + + return document + + # Configure JSON document SET to send + def configDocErase(self): + timeSent = int(time()) + + data = {} + data['time'] = timeSent + data['id'] = self.pubkey + data['issuer'] = self.pubkey + data['version'] = 2 + data['index'] = "user" + data['type'] = "profile" + + document = json.dumps(data) + + return self.signDoc(document) + + def sendDocument(self, document, type): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + if type == 'set': + reqQuery = '{0}/user/profile?pubkey={1}/_update?pubkey={1}'.format(self.pod, self.pubkey) + elif type == 'get': + reqQuery = '{0}/user,page,group/profile,record/_search'.format(self.pod) + elif type == 'erase': + reqQuery = '{0}/history/delete'.format(self.pod) + + result = requests.post(reqQuery, headers=headers, data=document) + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document...\n" + result.text + '\n') + + def parseJSON(self, doc): + doc = json.loads(doc)['hits']['hits'] + if doc: + pubkey = { "pubkey": doc[0]['_id'] } + rest = doc[0]['_source'] + final = {**pubkey, **rest} + return json.dumps(final, indent=2) + else: + return 'Profile vide' diff --git a/zen/jaklis.old/paiements.py b/zen/jaklis.old/paiements.py new file mode 100755 index 0000000..dde58da --- /dev/null +++ b/zen/jaklis.old/paiements.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +""" +ZetCode Tkinter tutorial + +In this example, we use the pack +manager to create a review example. + +Author: Jan Bodnar +Website: www.zetcode.com +""" + +import PySimpleGUI as sg +from lib.gva import GvaApi +import sys, os, threading +from shutil import copyfile +from os.path import join, dirname +from dotenv import load_dotenv +from lib.natools import get_privkey +import requests + +class StdoutRedirector(object): + def __init__(self, text_widget): + self.text_widget = text_widget + + def write(self, s): + self.text_widget.insert('end', s) + self.text_widget.see('end') + + def flush(self): + pass + + +MY_PATH = os.path.realpath(os.path.dirname(sys.argv[0])) + '/' + +# Get variables environment +if not os.path.isfile(MY_PATH + '.env'): + copyfile(MY_PATH + ".env.template",MY_PATH + ".env") +dotenv_path = join(dirname(__file__),MY_PATH + '.env') +load_dotenv(dotenv_path) + +dunikey = os.getenv('DUNIKEY') +if not os.path.isfile(dunikey): + HOME = os.getenv("HOME") + dunikey = HOME + dunikey + if not os.path.isfile(dunikey): + sys.stderr.write('Le fichier de trousseau {0} est introuvable.\n'.format(dunikey)) + sys.exit(1) +node = os.getenv('NODE') +issuer = get_privkey(dunikey, "pubsec").pubkey + + +def ProceedPaiement(recipient, amount, comment): + if not recipient: + raise ValueError("Veuillez indiquer un destinataire de paiement") + elif not amount: + raise ValueError("Veuillez indiquer le montant de la transaction") + + amount = int(float(amount.replace(',','.'))*100) + print("Paiement en cours vers", recipient) + gva = GvaApi(dunikey, node, recipient) + gva.pay(amount, comment, False, False) + + recipient = amount = comment = None + + +sg.theme('DarkGrey2') +layout = [ [sg.Text('Noeud utilisé: ' + node)], + [sg.Text('Votre clé publique: ' + issuer)], + [sg.Text('')], + [sg.Text('Destinataire: '), sg.InputText(size=(55, None),default_text=issuer)], + [sg.Text('Montant: '), sg.InputText(size=(7, None)), sg.Text('Ḡ1')], + [sg.Text('Commentaire:'), sg.InputText(size=(55, None))], + [sg.Button('Envoyer')] ] + +# Create the Window +window = sg.Window('Paiement Ḡ1 - GVA', layout) +# availablePubkeys = requests.get('https://g1-stats.axiom-team.fr/data/wallets-g1.txt') +while True: + try: + event, values = window.read() + if event == sg.WIN_CLOSED: + break + if event == 'Envoyer': + ProceedPaiement(values[0], values[1], values[2]) + except Exception as e: + loc = window.CurrentLocation() + sg.popup(e, title="ERREUR", button_color=('black','red'), location=(loc)) + else: + loc = window.CurrentLocation() + sg.popup(f'Transaction effectué avec succès !', title="Envoyé", location=(loc)) + + +window.close() diff --git a/zen/jaklis.old/requirements.txt b/zen/jaklis.old/requirements.txt new file mode 100644 index 0000000..e6b5711 --- /dev/null +++ b/zen/jaklis.old/requirements.txt @@ -0,0 +1,7 @@ +wheel +base58 +pybase64 +duniterpy +termcolor +python-dotenv +gql diff --git a/zen/jaklis.old/setup.sh b/zen/jaklis.old/setup.sh new file mode 100755 index 0000000..222e4ba --- /dev/null +++ b/zen/jaklis.old/setup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel; do + if [ $(dpkg-query -W -f='${Status}' $i 2>/dev/null | grep -c "ok installed") -eq 0 ]; then + [[ ! $j ]] && sudo apt update + sudo apt install -y $i + j=1 + fi +done + +pip3 install -r requirements.txt +chmod u+x jaklis.py diff --git a/zen/jaklis/.env.template b/zen/jaklis/.env.template old mode 100644 new mode 100755 diff --git a/zen/jaklis/README.md b/zen/jaklis/README.md old mode 100644 new mode 100755 index 59f1e61..e0ff54d --- a/zen/jaklis/README.md +++ b/zen/jaklis/README.md @@ -13,6 +13,8 @@ Débrouillez-vous. ## Utilisation +*Python 3.9 minimum* + Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template). ``` diff --git a/zen/jaklis/jaklis.py b/zen/jaklis/jaklis.py index 0f40166..24ffc17 100755 --- a/zen/jaklis/jaklis.py +++ b/zen/jaklis/jaklis.py @@ -29,8 +29,11 @@ delete_cmd = subparsers.add_parser('delete', help="Supression d'un message") getProfile_cmd = subparsers.add_parser('get', help="Voir un profile Cesium+") setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesium+") eraseProfile_cmd = subparsers.add_parser('erase', help="Effacer son profile Cesium+") -like_cmd = subparsers.add_parser('like', help="Voir les likes d'un profile / Liker un profile (option -s NOTE)") -unlike_cmd = subparsers.add_parser('unlike', help="Supprimer un like") +stars_cmd = subparsers.add_parser('stars', help="Voir les étoiles d'un profile / Noter un profile (option -s NOTE)") +unstars_cmd = subparsers.add_parser('unstars', help="Supprimer un star") +getoffer_cmd = subparsers.add_parser('getoffer', help="Obtenir les informations d'une annonce gchange") +setoffer_cmd = subparsers.add_parser('setoffer', help="Créer une annonce gchange") +deleteoffer_cmd = subparsers.add_parser('deleteoffer', help="Supprimer une annonce gchange") pay_cmd = subparsers.add_parser('pay', help="Payer en Ḡ1") history_cmd = subparsers.add_parser('history', help="Voir l'historique des transactions d'un compte Ḡ1") balance_cmd = subparsers.add_parser('balance', help="Voir le solde d'un compte Ḡ1") @@ -62,9 +65,20 @@ getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile") getProfile_cmd.add_argument('-a', '--avatar', action='store_true', help="Récupérer également l'avatar au format raw base64") # Likes management -like_cmd.add_argument('-p', '--profile', help="Profile cible") -like_cmd.add_argument('-s', '--stars', type=int, help="Nombre d'étoile") -unlike_cmd.add_argument('-p', '--profile', help="Profile à déliker") +stars_cmd.add_argument('-p', '--profile', help="Profile cible") +stars_cmd.add_argument('-n', '--number', type=int, help="Nombre d'étoile") +unstars_cmd.add_argument('-p', '--profile', help="Profile à dénoter") + +# Offers management +getoffer_cmd.add_argument('-i', '--id', help="Annonce cible à récupérer") +setoffer_cmd.add_argument('-t', '--title', help="Titre de l'annonce à créer") +setoffer_cmd.add_argument('-d', '--description', help="Description de l'annonce à créer") +setoffer_cmd.add_argument('-c', '--category', help="Categorie de l'annonce à créer") +setoffer_cmd.add_argument('-l', '--localisation', nargs=2, help="Localisation de l'annonce à créer (lat + lon)") +setoffer_cmd.add_argument('-p', '--picture', help="Image de l'annonce à créer") +setoffer_cmd.add_argument('-ci', '--city', help="Ville de l'annonce à créer") +setoffer_cmd.add_argument('-pr', '--price', help="Prix de l'annonce à créer") +deleteoffer_cmd.add_argument('-i', '--id', help="Annonce cible à supprimer") # GVA usage pay_cmd.add_argument('-p', '--pubkey', help="Destinataire du paiement") @@ -74,6 +88,7 @@ pay_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les s pay_cmd.add_argument('-v', '--verbose', action='store_true', help="Affiche le résultat JSON de la transaction") history_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé") +history_cmd.add_argument('-n', '--number',type=int, default=10, help="Affiche les NUMBER dernières transactions") history_cmd.add_argument('-j', '--json', action='store_true', help="Affiche le résultat en format JSON") history_cmd.add_argument('--nocolors', action='store_true', help="Affiche le résultat en noir et blanc") @@ -143,7 +158,7 @@ else: # Construct CesiumPlus object -if cmd in ("read","send","delete","set","get","erase","like","unlike"): +if cmd in ("read","send","delete","set","get","erase","stars","unstars","getoffer","setoffer","deleteoffer"): from lib.cesium import CesiumPlus if args.node: @@ -187,15 +202,23 @@ if cmd in ("read","send","delete","set","get","erase","like","unlike"): elif cmd == "erase": cesium.erase() - # Likes - elif cmd == "like": - if args.stars or args.stars == 0: - cesium.like(args.stars, args.profile) + # Stars + elif cmd == "stars": + if args.number or args.number == 0: + cesium.like(args.number, args.profile) else: cesium.readLikes(args.profile) - elif cmd == "unlike": + elif cmd == "unstars": cesium.unLike(args.profile) + # Offers + elif cmd == "getoffer": + cesium.getOffer(args.id) + elif cmd == "setoffer": + cesium.setOffer(args.title, args.description, args.city, args.localisation, args.category, args.price, args.picture) + elif cmd == "deleteoffer": + cesium.deleteOffer(args.id) + # Construct GVA object elif cmd in ("pay","history","balance"): from lib.gva import GvaApi @@ -217,7 +240,7 @@ elif cmd in ("pay","history","balance"): if cmd == "pay": gva.pay(args.amount, args.comment, args.mempool, args.verbose) if cmd == "history": - gva.history(args.json, args.nocolors) + gva.history(args.json, args.nocolors, args.number) if cmd == "balance": gva.balance(args.mempool) diff --git a/zen/jaklis/lib/__pycache__/cesium.cpython-36.pyc b/zen/jaklis/lib/__pycache__/cesium.cpython-36.pyc index 08355fcf90d13f720aade39c9885b41a02edf266..b28451597ef7d40b68f322717c51cce12d6bf753 100644 GIT binary patch delta 1863 zcmZ`)&2Jk;6yI6zx7W_M`$Zv58pO6J2Lc2t2uVs40j(1eA=IUjaXb^V&f1P<*M|m2 zIoMPP4yYOli35oLpyI$CA+DT}R)`CK1{dC&jn_^HE6>y0ncx22d%t-z{^QEGv-3-Z zg7d@j>!1EQp(uYVBaa6DDvYTAbiO`E6>5YN%}IZfD4Pm3snu7g#q?gzpQ2<(@$9ER z_HKA2F}C*i8INisOo}<6Iudut%}=gTi`sqS8}vEq&^&l1b!nlm_!hlR$LKiB*z|ci zK_|g;=nHg;PJ@@Hvvh`*z;o#hdWl{JuRyQRtMFx!nvlet_@4Yjro?yZI&sCH>hDGY zLi3_{mg|2s2Cipws8dPwIEX`*O?FX%D_-?z# zrw}L2G_Gj~o)KSaPj=HFxO@|h3}nbaz7x>sK~%pVL~$C!r7UDIOQZODKw*`?lu-}) zjTAU14)m`UvIvP=Yq$1;IwZ8iVi}5$!`EJ(Pl&6=?)2F#`E6LcExtB>CJSQCoEhlx z$Xt05(@gBcDv#LvJ6q+H#!B@u4|#h(2pL}zznGV9y^2tWv#1ls68joVoD+mE!`zY} z){~Ei*%q^G@9}EHM)K8CeO!j~+o#bm4!?+d6&BXTUsiuFY9rSrP(%(qD2Wny%N=X6ENxiQE%@@kd%6a*d=rh_$I~t^!-Vh1(=^-`O#kHk zK~3`M&62|4v(q|?#34oXs5G)UBS6nArw?dd_Q=NRFPkN0Z!EDYl~%RMDwV{kRGMwt z30Xxv%a5PTV7eX}tVuSj+%Ue1uXoYhL$iSf+lHga95!S$`|NV$E-aw>5nkSwX4zK3 zvK`AAlh$z1w<=hDf3N9L4 z2rA`*JO6Q{ATLi>TquY|W3}{el`tV=6XK)sZg@;u zetIpSQM%5;N)~YPusfd@=FAf;+qbu>z}fuDjq5S370=7ug4TM_U~mGH97(VgJA9qj zT3ZZGDnBsZZiMReB(m=-=9KhdA}>kMNRecSL57Ht%A|5cELhE3NuRlHpO>M^*`7`_ z@nk)Q{@pT^J{+VuEtTd(!S3Aaa>JDLC9CpX)$_8dJ?>gp5^z@LMS3IHVEn%VkqxX$ z7cpmE{IVZiNo11C5c5Sk59M?fEu6y|a2WfB6lG9vS-f{1-|15McI`f^ZJZ4n3tDpp1E1VMvkp)04q-f(*OVf delta 17 YcmdnXyO)>4n3tF9gTmGLjT~Fq05J0ftN;K2 diff --git a/zen/jaklis/lib/__pycache__/likes.cpython-36.pyc b/zen/jaklis/lib/__pycache__/likes.cpython-36.pyc index ad140ef85144a9fc5b66567a504270aa57e89257..4b0067888a8e428b1be9defb4af4b14d2305b291 100644 GIT binary patch delta 17 YcmZ3ezfhmUn3tDpp1E1VMvj@{04t;f)Bpeg delta 17 YcmZ3ezfhmUn3tF9gTmGLjT|$@0WnAgtpET3 diff --git a/zen/jaklis/lib/__pycache__/messaging.cpython-36.pyc b/zen/jaklis/lib/__pycache__/messaging.cpython-36.pyc index 403471631f6b95b1973a6ca7fe816d5fa85026b8..7134b56b6feb2033d4c722a332e1ad4461bfa851 100644 GIT binary patch delta 17 YcmaED^4f&Mn3tDpp1E1VMvi1D05uT>KL7v# delta 17 YcmaED^4f&Mn3tF9gTmGLjU35R06Mh>7ytkO diff --git a/zen/jaklis/lib/__pycache__/natools.cpython-36.pyc b/zen/jaklis/lib/__pycache__/natools.cpython-36.pyc index 06f0acf22e23154bcb70434511e98ec4540cf9b9..dca2c57c994ba8907706bf48f3363e7bee15d085 100644 GIT binary patch delta 17 YcmccMalwPbn3tDpp1E1VMh*`Z05jbMAOHXW delta 17 YcmccMalwPbn3tF9gTmGLjT{~-06BpL`2YX_ diff --git a/zen/jaklis/lib/__pycache__/offers.cpython-36.pyc b/zen/jaklis/lib/__pycache__/offers.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e5d84fd873590c06c9118d827c69f07aabc77b8 GIT binary patch literal 3538 zcmai1%W@mX6`l7C9;B$ZDN8bJ%d$~5By3rxlZbX0k(3fSMJb|_Qm{dW(@k=~!3^Bp zkV6<;B-B<_S*P>^`4j(uQ>or&#b3xO=k|aUsW|09^}V<6^z`%Ga~q$|%+&AQTl&)v zmn`ew)~UyXy@Dd2K`@Kixz(+--L*~K={iQcUDs%@>p?rYKk&Q0O>Nyg7=+z$5OpKl zdT23^`7bQy^YGB^)|kC%wSyz{Zf&$}?Wt_Q+m3Foa+!?=tL0!&7P|T5_xHbA|6}jz z`or~SDYZR*To@}Tav6eKG^eh^U1qUs&CM&9EA)zp%R@Yp^Cf z5sNT~Ssgsy-sVDL7XDCVDNiHxNlz2SM_EOY&uq(5ma-46L;E1gtb^Lb7S|^B1WQ@A zCXTp0abP>JU2$jP!ggVMqKmfm38ph|P3p`i9}A2f!mguT1RaXMD`yh2+M&~T53Pex zd3}Ep9@_nYYMF(0b!ta?-eQeqi#4bA4D4BUj?JN*f8|16U>9F`czX$71mb%YPJ%<5 zUA{!UTtVxr&~M>g1U*01*PyLFdEP#;uA}V@cy2t4!rwHyD?x$c8Gz`Fm-m z_60WH(r%upqp5{A(GD-4lv$y)BeuFVwL2PY6-k!ssF#+7;)NR5vO%)LJHui}&m22@ zYJbSPewwH|cgKyjKdiMntp`~?kftJ0Tg?jIcD2KcvCrkg`sUc*77GuaRv&%V_H-~z z1p4pJ=4G1X*_gA9WWc5N3MlPlOh@RS@04Qyh+*m?p!xeHfebju1cXDLW-53>%lsP9 z*Fh%b2n~YaXlsw}ADJfMbKsIT!rA!*y^aJ1PWQDpDl(k$mZ{u5B&dMfWAcWEeNv-_FZKiMMIG zUSDD&J{t{&Qin3zDb~sqoqCez+ajceQrw7|SzEO?(yUKhCcoK*a5_I7QM_+r8@0z0 zm53SoDq>|i8o>DvODw_hpU*qH<$!m#1!tXaOR*=1NyaFFO5XFVAEr z&$c>di{;{QAA4IC+u06gxXBgGO>RI~cHQ%A&x&3b+q0Ya!DG+j7g5c-hWB%@=~u_E zvPcHjwC2>E@x_Wa7Eh0r(z(&oKp~Pc-0@EsC*0ux_s+y-_Mvm&D!1>U?oZr98!_R5 z8=QITz=w9BgNio}LTDfLig$cO67dZ24RH*`TmVg6M4Un#;XzX@gKrq=8Z>duF>erO zP(;p7^|`5@gFX-cg{i(cja)DZ=8BOp%Q|KpPUe1}a~dYdq(Q-xnj*ojK#G)?*g%dOdfk zlg~&HIxF!xEIoSyfpvjkqIU8Q1OOK_?Kx)(AjdlerPrW3u?SR`-vpKEF(oL`Ls3Mz z*}&?rFc|V(W*@i{WW7B{++<##XxxCQh~f3&U1CAyoe=1};u8EJ{Et}ly0t>$nRpYI z)m}OPpx^gc9c^}D`J0vs`e8p}4XRy5>bZ(ENLe1_J+m=xu2!VGK=d}AONPTd0||n< zI)*k}$<_G$cOM^g28r0?sv|%!f{CAF1>=AHkH7QXCkMam$#Nu8{>3qw#wJ0;r}9Hw zaVB02czl{oW1=4#Jx&+{8_JjxNo=xvOyn6Kb7_2%ITp|4i1-YV# z#>c5BuBbZ(UGXP$kL-s? z+()x^O;9|GH4^I(+8dTgau2Bfki@S^{D#CMh;~?=p`jvkhKAWz=>1nDeoNvFjyl7d z$chEp$O{nFVNwp9b>{y6LE@8>H{xd`z92!^ z7mrEMNT)_OTTB^15UQ!jIz;5mE=0GYEA@-U->#eU!%dY}i=rbIZ4xG*&Ef8{n4bFI fBZ%z