new jaklis
This commit is contained in:
parent
d789e0ff94
commit
39d3bb720c
|
@ -166,7 +166,7 @@ done
|
|||
|
||||
echo 'SEND ipfstryme to oasis' # Add your bootstrap Pub here
|
||||
./jaklis.py -n "https://g1.data.le-sou.org" send -d 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr
|
||||
./jaklis.py -n "https://data.gchange.fr send" -d 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr
|
||||
./jaklis.py -n "https://data.gchange.fr" send -d 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr
|
||||
|
||||
########################################################################
|
||||
echo 'INSTALL Astroport cron_MINUTE'
|
||||
|
|
|
@ -57,12 +57,18 @@ do
|
|||
echo $ip >> ~/.zen/A_allow_ip.txt
|
||||
# Get its ipfsnodeid
|
||||
ipfsnodeid=$(echo "$peer" | awk -F '/' '{print $7}')
|
||||
|
||||
# 3. ADD liking_me friend_of_mine to my swarm & bootstrap
|
||||
ipfs swarm connect $peer;
|
||||
ipfs bootstrap add $peer;
|
||||
echo $ipfsnodeid >> ~/.zen/A_allow_ipfsid.txt
|
||||
|
||||
g1id=$(~/.zen/astrXbian/zen/tools/ipfs_to_g1.py "$ipfsnodeid")
|
||||
if [[ "$g1id" == "$friend_of_mine" ]]; then
|
||||
# 3. ADD liking_me friend_of_mine to my swarm & bootstrap
|
||||
ipfs swarm connect $peer;
|
||||
ipfs bootstrap add $peer;
|
||||
|
||||
ipfsadd=$((ipfsadd+1))
|
||||
ipfsadd=$((ipfsadd+1))
|
||||
else
|
||||
echo "$friend_of_mine spoofing is happening"
|
||||
fi
|
||||
done;
|
||||
|
||||
#g1id=$(~/.zen/astrXbian/zen/tools/ipfs_to_g1.py "$ipfsnodeid")
|
||||
|
|
|
@ -46,9 +46,10 @@ do
|
|||
|
||||
echo "$nowdate - $id - $ip"
|
||||
foundIp=$(cat ~/.zen/A_allow_ip.txt | grep "$ip")
|
||||
foundIpfs=$(cat ~/.zen/A_allow_ipfsid.txt | grep "$ipfsnodeid")
|
||||
isLAN=$(echo $ip | cut -f3 -d '/' | grep -E "(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/")
|
||||
|
||||
if [[ ! $foundIp && ! $isLAN ]] ; then
|
||||
if [[ ! $foundIpfs && ! $isLAN ]] ; then
|
||||
echo "${ip} of peer ${id} is not in the authorized ip list."
|
||||
echo "${peer} will be removed from the swarm"
|
||||
|
||||
|
@ -56,10 +57,12 @@ do
|
|||
ipfs bootstrap rm $peer
|
||||
|
||||
echo "# FAIL2BAN # $USER must activate no password sudo (Rpi & Xbian OK)"
|
||||
[[ $USER == "pi" || $USER == "xbian" ]] && echo "BAN $ip" \
|
||||
&& sudo fail2ban-client add recidive \
|
||||
&& sudo fail2ban-client set recidive banip $ip
|
||||
|
||||
if [[ $USER == "pi" || $USER == "xbian" ]]; then
|
||||
echo "BAN $ip ($ipfsnodeid)"
|
||||
sudo fail2ban-client add recidive
|
||||
sudo fail2ban-client start recidive
|
||||
sudo fail2ban-client set recidive banip $ip
|
||||
fi
|
||||
else
|
||||
echo "${peer}"
|
||||
echo "REFRESH /ipns/$ipfsnodeid INTO ~/.zen/ipfs_swarm/"
|
||||
|
|
|
@ -1,4 +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
|
||||
#POD="https://data.gchange.fr" # Adresse du pod ḠChange à utiliser
|
||||
|
||||
NODE="https://g1.librelois.fr/gva"
|
||||
|
|
|
@ -20,10 +20,18 @@ Renseignez optionnellement le fichier **.env** (Généré lors de la première t
|
|||
```
|
||||
|
||||
```
|
||||
usage: jaklis.py [-h] [-v] [-k KEY] [-n NODE] {read,send,delete,get,set,erase,like,unlike} ...
|
||||
usage: jaklis.py [-h] [-v] [-k KEY] [-n NODE] {read,send,delete,get,set,erase,like,unlike,pay,history,balance} ...
|
||||
|
||||
positional arguments:
|
||||
{read,send,delete,get,set,erase,like,unlike}
|
||||
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
|
||||
|
@ -32,12 +40,10 @@ positional arguments:
|
|||
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
|
||||
|
||||
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+ ou Gchange à utiliser
|
||||
```
|
||||
|
||||
Utilisez `./jaklis CMD -h` où `CMD` est la commande souhaité pour obtenir l'aide détaillé de cette commande.
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse, sys, os, random, string, getpass, json
|
||||
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
|
||||
from lib.cesium import ReadFromCesium, SendToCesium, DeleteFromCesium, Profiles
|
||||
from lib.likes import ReadLikes, SendLikes, UnLikes
|
||||
|
||||
VERSION = "0.0.1"
|
||||
__version__ = "0.0.2"
|
||||
|
||||
MY_PATH = os.path.realpath(os.path.dirname(sys.argv[0])) + '/'
|
||||
|
||||
# Get variables environment
|
||||
if not os.path.isfile('.env'):
|
||||
copyfile(".env.template", ".env")
|
||||
dotenv_path = join(dirname(__file__), '.env')
|
||||
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+ ou Gchange à utiliser")
|
||||
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")
|
||||
|
@ -31,6 +31,9 @@ setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesiu
|
|||
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")
|
||||
|
@ -63,17 +66,32 @@ 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)
|
||||
|
||||
if args.version:
|
||||
print(VERSION)
|
||||
sys.exit(0)
|
||||
|
||||
def createTmpDunikey():
|
||||
# Generate pseudo-random nonce
|
||||
nonce=[]
|
||||
|
@ -87,79 +105,121 @@ def createTmpDunikey():
|
|||
|
||||
return keyPath
|
||||
|
||||
if args.node:
|
||||
pod = args.node
|
||||
else:
|
||||
pod = os.getenv('POD')
|
||||
if not pod:
|
||||
pod="https://g1.data.le-sou.org"
|
||||
# Check if we need dunikey
|
||||
try:
|
||||
pubkey = args.pubkey
|
||||
except:
|
||||
pubkey = False
|
||||
try:
|
||||
profile = args.profile
|
||||
except:
|
||||
profile = False
|
||||
|
||||
if args.key:
|
||||
dunikey = args.key
|
||||
if cmd in ('history','balance','get') and (pubkey or profile):
|
||||
noNeedDunikey = True
|
||||
keyPath = False
|
||||
try:
|
||||
dunikey = args.pubkey
|
||||
except:
|
||||
dunikey = args.profile
|
||||
else:
|
||||
dunikey = os.getenv('DUNIKEY')
|
||||
if not dunikey:
|
||||
keyPath = createTmpDunikey()
|
||||
dunikey = keyPath
|
||||
else:
|
||||
noNeedDunikey = False
|
||||
if args.key:
|
||||
dunikey = args.key
|
||||
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)
|
||||
|
||||
|
||||
# Build cesiumMessaging class
|
||||
if cmd == "read":
|
||||
messages = ReadFromCesium(dunikey, pod)
|
||||
messages.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: ")
|
||||
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)
|
||||
|
||||
messages = SendToCesium(dunikey, pod, args.destinataire, args.outbox)
|
||||
messages.send(titre, msg)
|
||||
|
||||
elif cmd == "delete":
|
||||
messages = DeleteFromCesium(dunikey, pod, args.outbox)
|
||||
messages.delete(args.id[0])
|
||||
# Construct CesiumPlus object
|
||||
if cmd in ("read","send","delete","set","get","erase","like","unlike"):
|
||||
from lib.cesium import CesiumPlus
|
||||
|
||||
# Build cesium+ profiles class
|
||||
elif cmd in ('set','get','erase'):
|
||||
cesium = Profiles(dunikey, pod)
|
||||
if cmd == "set":
|
||||
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()
|
||||
|
||||
# Build cesium+ likes class
|
||||
elif cmd == "like":
|
||||
if args.stars or args.stars == 0:
|
||||
gchange = SendLikes(dunikey, pod)
|
||||
gchange.like(args.stars, args.profile)
|
||||
# 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:
|
||||
gchange = ReadLikes(dunikey, pod)
|
||||
gchange.readLikes(args.profile)
|
||||
elif cmd == "unlike":
|
||||
gchange = UnLikes(dunikey, pod)
|
||||
gchange.unLike(args.profile)
|
||||
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:
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,540 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
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
|
||||
|
||||
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
|
||||
class CesiumPlus(CesiumCommon):
|
||||
|
||||
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 ReadFromCesium:
|
||||
def __init__(self, dunikey, pod):
|
||||
# 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)
|
||||
|
||||
self.recipient = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
|
||||
if not re.match(PUBKEY_REGEX, self.recipient) or len(self.recipient) > 45:
|
||||
sys.stderr.write("La clé publique n'est pas au bon format.\n")
|
||||
sys.exit(1)
|
||||
|
||||
# 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.recipient
|
||||
|
||||
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
|
||||
#################### Messaging ####################
|
||||
|
||||
def read(self, nbrMsg, outbox, isJSON):
|
||||
jsonMsg = self.sendDocument(nbrMsg, outbox)
|
||||
readCesium = ReadFromCesium(self.dunikey, self.pod)
|
||||
jsonMsg = readCesium.sendDocument(nbrMsg, outbox)
|
||||
if isJSON:
|
||||
jsonFormat = self.jsonMessages(jsonMsg, nbrMsg, outbox)
|
||||
jsonFormat = readCesium.jsonMessages(jsonMsg, nbrMsg, outbox)
|
||||
print(jsonFormat)
|
||||
else:
|
||||
self.readMessages(jsonMsg, nbrMsg, outbox)
|
||||
readCesium.readMessages(jsonMsg, nbrMsg, outbox)
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Sending class ####################
|
||||
|
||||
|
||||
|
||||
|
||||
class SendToCesium:
|
||||
def __init__(self, dunikey, pod, recipient, outbox):
|
||||
# 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)
|
||||
|
||||
self.issuer = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
self.recipient = recipient
|
||||
self.outbox = 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))
|
||||
self.nonce = base64.b64decode(''.join(nonce))
|
||||
sendCesium.nonce = base64.b64decode(''.join(nonce))
|
||||
|
||||
if not re.match(PUBKEY_REGEX, recipient) or len(recipient) > 45:
|
||||
sys.stderr.write("La clé publique n'est pas au bon format.\n")
|
||||
sys.exit(1)
|
||||
finalDoc = sendCesium.configDoc(sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg)) # Configure JSON document to send
|
||||
sendCesium.sendDocument(finalDoc, outbox) # Send final signed document
|
||||
|
||||
|
||||
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.time())
|
||||
|
||||
# Generate custom JSON
|
||||
data = {}
|
||||
data['issuer'] = self.issuer
|
||||
data['recipient'] = self.recipient
|
||||
data['title'] = title
|
||||
data['content'] = msg
|
||||
data['time'] = timeSent
|
||||
data['nonce'] = b58nonce
|
||||
data['version'] = 2
|
||||
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
|
||||
finalDoc = '{' + '"hash":"{0}","signature":"{1}",'.format(hashDoc, signature) + document[1:]
|
||||
|
||||
return finalDoc
|
||||
|
||||
|
||||
def sendDocument(self, document):
|
||||
boxType = "outbox" if self.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')
|
||||
|
||||
def send(self, title, msg):
|
||||
finalDoc = self.configDoc(self.encryptMsg(title), self.encryptMsg(msg)) # Configure JSON document to send
|
||||
self.sendDocument(finalDoc) # Send final signed document
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Deleting class ####################
|
||||
|
||||
|
||||
|
||||
|
||||
class DeleteFromCesium:
|
||||
def __init__(self, dunikey, pod, outbox):
|
||||
# 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)
|
||||
|
||||
self.issuer = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
self.outbox = outbox
|
||||
|
||||
|
||||
def configDoc(self, idMsg):
|
||||
# Get current timestamp
|
||||
timeSent = int(time.time())
|
||||
|
||||
boxType = "outbox" if self.outbox else "inbox"
|
||||
|
||||
# Generate document to customize
|
||||
data = {}
|
||||
data['version'] = 2
|
||||
data['index'] = "message"
|
||||
data['type'] = boxType
|
||||
data['id'] = idMsg
|
||||
data['issuer'] = self.issuer
|
||||
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, 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.")
|
||||
|
||||
def delete(self, idsMsgList):
|
||||
def delete(self, idsMsgList, outbox):
|
||||
deleteCesium = DeleteFromCesium(self.dunikey, self.pod)
|
||||
# deleteCesium.issuer = recipient
|
||||
for idMsg in idsMsgList:
|
||||
finalDoc = self.configDoc(idMsg)
|
||||
self.sendDocument(finalDoc, idMsg)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Profile class ####################
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Profiles:
|
||||
def __init__(self, dunikey, pod):
|
||||
# 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)
|
||||
|
||||
self.pubkey = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
|
||||
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)
|
||||
|
||||
# Configure JSON document SET to send
|
||||
def configDocSet(self, name, description, city, address, pos, socials, avatar):
|
||||
timeSent = int(time.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)
|
||||
|
||||
# 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
|
||||
|
||||
# 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.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)
|
||||
|
||||
# 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, 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}
|
||||
else:
|
||||
final = 'Profile vide'
|
||||
|
||||
return json.dumps(final, indent=2)
|
||||
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):
|
||||
document = self.configDocSet(name, description, ville, adresse, position, site, avatar)
|
||||
result = self.sendDocument(document,'set')
|
||||
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:
|
||||
|
@ -542,16 +56,38 @@ class Profiles:
|
|||
else:
|
||||
scope = '_id'
|
||||
|
||||
document = self.configDocGet(profile, scope, avatar)
|
||||
resultJSON = self.sendDocument(document, 'get')
|
||||
result = self.parseJSON(resultJSON)
|
||||
document = getProfile.configDocGet(profile, scope, avatar)
|
||||
resultJSON = getProfile.sendDocument(document, 'get')
|
||||
result = getProfile.parseJSON(resultJSON)
|
||||
|
||||
print(result)
|
||||
return result
|
||||
|
||||
def erase(self):
|
||||
document = self.configDocErase()
|
||||
result = self.sendDocument(document,'erase')
|
||||
eraseProfile = Profiles(self.dunikey, self.pod)
|
||||
document = eraseProfile.configDocErase()
|
||||
result = eraseProfile.sendDocument(document,'erase')
|
||||
|
||||
print(result)
|
||||
return 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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,40 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
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
|
||||
|
||||
PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}"
|
||||
|
||||
class ReadLikes:
|
||||
def __init__(self, dunikey, pod):
|
||||
# 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)
|
||||
|
||||
self.issuer = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
|
||||
if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45:
|
||||
sys.stderr.write("La clé publique n'est pas au bon format.\n")
|
||||
sys.exit(1)
|
||||
|
||||
class ReadLikes(CesiumCommon):
|
||||
# Configure JSON document to send
|
||||
def configDoc(self, profile):
|
||||
if not profile: profile = self.issuer
|
||||
# elif len(profile) < 42:
|
||||
# print(len(profile))
|
||||
# gProfile = requests.get('{0}/user/profile/{1}'.format(self.pod, issuer))
|
||||
# gProfile = json.loads(gProfile.text)['_source']
|
||||
# pseudo = gProfile['title']
|
||||
if not profile: profile = self.pubkey
|
||||
|
||||
data = {}
|
||||
data['query'] = {}
|
||||
|
@ -98,7 +73,7 @@ class ReadLikes:
|
|||
payTo = ''
|
||||
id = i['_id']
|
||||
level = i['_source']['level']
|
||||
if issuer == self.issuer:
|
||||
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 })
|
||||
|
@ -124,47 +99,18 @@ class ReadLikes:
|
|||
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'][0]['_source']
|
||||
|
||||
return result
|
||||
|
||||
def readLikes(self, profile=False):
|
||||
document = self.configDoc(profile)
|
||||
result = self.sendDocument(document)
|
||||
result = self.parseResult(result)
|
||||
|
||||
print(result)
|
||||
return result
|
||||
|
||||
|
||||
result = json.loads(result.text)['hits']['hits']
|
||||
for i in result:
|
||||
return i['_source']
|
||||
|
||||
|
||||
#################### Like class ####################
|
||||
|
||||
|
||||
|
||||
|
||||
class SendLikes:
|
||||
def __init__(self, dunikey, pod):
|
||||
# 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)
|
||||
|
||||
self.issuer = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
|
||||
if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45:
|
||||
sys.stderr.write("La clé publique n'est pas au bon format.\n")
|
||||
sys.exit(1)
|
||||
|
||||
class SendLikes(CesiumCommon):
|
||||
# Configure JSON document to send
|
||||
def configDoc(self, profile, likes):
|
||||
if not profile: profile = self.issuer
|
||||
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
|
||||
|
@ -180,7 +126,7 @@ class SendLikes:
|
|||
data['kind'] = "STAR"
|
||||
data['level'] = likes
|
||||
data['time'] = timeSent
|
||||
data['issuer'] = self.issuer
|
||||
data['issuer'] = self.pubkey
|
||||
|
||||
document = json.dumps(data)
|
||||
|
||||
|
@ -216,7 +162,10 @@ class SendLikes:
|
|||
resultJson = json.loads(result.text)
|
||||
if 'DuplicatedDocumentException' in resultJson['error']:
|
||||
rmLike = UnLikes(self.dunikey, self.pod)
|
||||
rmLike.unLike(pubkey, True)
|
||||
idLike = rmLike.checkLike(pubkey)
|
||||
if idLike:
|
||||
document = rmLike.configDoc(idLike)
|
||||
rmLike.sendDocument(document, True)
|
||||
sleep(0.5)
|
||||
self.sendDocument(document, pubkey)
|
||||
return resultJson['error']
|
||||
|
@ -227,42 +176,12 @@ class SendLikes:
|
|||
sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n')
|
||||
|
||||
|
||||
|
||||
|
||||
def like(self, stars, profile=False):
|
||||
document = self.configDoc(profile, stars)
|
||||
if document:
|
||||
self.sendDocument(document, profile)
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Unlike class ####################
|
||||
|
||||
|
||||
|
||||
|
||||
class UnLikes:
|
||||
def __init__(self, dunikey, pod):
|
||||
# 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)
|
||||
|
||||
self.issuer = get_privkey(dunikey, "pubsec").pubkey
|
||||
self.pod = pod
|
||||
|
||||
if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45:
|
||||
sys.stderr.write("La clé publique n'est pas au bon format.\n")
|
||||
sys.exit(1)
|
||||
|
||||
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)
|
||||
|
@ -285,7 +204,7 @@ class UnLikes:
|
|||
data['index'] = "like"
|
||||
data['type'] = "record"
|
||||
data['id'] = idLike
|
||||
data['issuer'] = self.issuer
|
||||
data['issuer'] = self.pubkey
|
||||
data['time'] = timeSent
|
||||
|
||||
document = json.dumps(data)
|
||||
|
@ -321,11 +240,3 @@ class UnLikes:
|
|||
return result.text
|
||||
else:
|
||||
sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n')
|
||||
|
||||
|
||||
def unLike(self, pubkey, silent=False):
|
||||
idLike = self.checkLike(pubkey)
|
||||
if idLike:
|
||||
document = self.configDoc(idLike)
|
||||
self.sendDocument(document, silent)
|
||||
|
||||
|
|
|
@ -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.")
|
|
@ -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'
|
|
@ -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()
|
|
@ -4,3 +4,4 @@ pybase64
|
|||
duniterpy
|
||||
termcolor
|
||||
python-dotenv
|
||||
gql
|
||||
|
|
Loading…
Reference in New Issue