Compare commits

..

No commits in common. "master" and "manageOffers" have entirely different histories.

22 changed files with 311 additions and 1261 deletions

View File

@ -1,16 +1,6 @@
# Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec DUNIKEY="" # Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec
# ex. DUNIKEY="/path/myprivateG1key.dunikey" #POD="https://g1.data.duniter.fr" # Adresse du pod Cesium ou Gchange à utiliser
DUNIKEY="" POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours
#POD="https://data.gchange.fr" # Adresse du pod ḠChange à utiliser
# Noeud Duniter NODE="https://g1.librelois.fr/gva"
DUNITER="https://g1.asycn.io"
#DUNITER="https://duniter.pini.fr"
#DUNITER="https://g1v1.p2p.legal"
# Adresse du pod Cesium ou Gchange à utiliser
ESNODE="https://g1.data.e-is.pro"
#ESNODE="https://g1.data.presles.fr"
#ESNODE="https://g1.data.adn.life"
#ESNODE=https://g1.data.le-sou.org
#ESNODE=https://g1.data.duniter.fr
#ESNODE=https://data.gchange.fr

5
.gitignore vendored
View File

@ -1,5 +1,4 @@
.env .env
*.dunikey
not4U
.vscode/settings.json
__pycache__ __pycache__
*.dunikey

View File

@ -1,7 +0,0 @@
{
"python.pythonPath": "/usr/bin/python3.9",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none"
}

View File

@ -13,7 +13,7 @@ Débrouillez-vous.
## Utilisation ## Utilisation
*Python 3.6 minimum* *Python 3.8 minimum*
Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template). Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template).
@ -22,7 +22,7 @@ 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,stars,unstars,getoffer,setoffer,deleteoffer,pay,history,balance,id,idBalance} ... 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 Client CLI pour Cesium+ et Ḡchange
@ -33,23 +33,19 @@ optional arguments:
-n NODE, --node NODE Adresse du noeud Cesium+, Gchange ou Duniter à utiliser -n NODE, --node NODE Adresse du noeud Cesium+, Gchange ou Duniter à utiliser
Commandes de jaklis: Commandes de jaklis:
{read,send,delete,get,set,erase,stars,unstars,getoffer,setoffer,deleteoffer,pay,history,balance,id,idBalance} {read,send,delete,get,set,erase,like,unlike,pay,history,balance}
read Lecture des messages read Lecture des messages
send Envoi d'un message send Envoi d'un message
delete Supression d'un message delete Supression d'un message
get Voir un profile Cesium+ get Voir un profile Cesium+
set Configurer son profile Cesium+ set Configurer son profile Cesium+
erase Effacer son profile Cesium+ erase Effacer son profile Cesium+
stars Voir les étoiles d'un profile / Noter un profile (option -s NOTE) like Voir les likes d'un profile / Liker un profile (option -s NOTE)
unstars Supprimer un star unlike Supprimer un like
getoffer Obtenir les informations d'une annonce gchange
setoffer Créer une annonce gchange
deleteoffer Supprimer une annonce gchange
pay Payer en Ḡ1 pay Payer en Ḡ1
history Voir l'historique des transactions d'un compte Ḡ1 history Voir l'historique des transactions d'un compte Ḡ1
balance Voir le solde d'un compte Ḡ1 balance Voir le solde d'un compte Ḡ1
id Voir l'identité d'une clé publique/username
idBalance Voir l'identité d'une clé publique/username et son solde
``` ```
Utilisez `./jaklis CMD -h``CMD` est la commande souhaité pour obtenir l'aide détaillé de cette commande. Utilisez `./jaklis CMD -h``CMD` est la commande souhaité pour obtenir l'aide détaillé de cette commande.

View File

@ -1,2 +0,0 @@
pub: HTkEecgbtBd3aZiJJYuUjGwLB7r3Ud232TG4BwLDdB3J
sec: 36fT3if1YGLwmhSS89F8vQ6QpmhWmhcLdQyHHmSgxJpbYSxq7C37sDmPKZykmGrE8zmeUxdyAiY256H852p3Lfor

557
jaklis.py
View File

@ -1,395 +1,184 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse, sys, os, getpass, string, random
import sys from os.path import join, dirname
import os from shutil import copyfile
import string
import random
from dotenv import load_dotenv from dotenv import load_dotenv
from duniterpy.key import SigningKey from duniterpy.key import SigningKey
from pathlib import Path
from lib.gva import GvaApi
from lib.cesium import CesiumPlus
__version__ = "0.1.1" __version__ = "0.0.2"
MY_PATH = Path(__file__).resolve().parent MY_PATH = os.path.realpath(os.path.dirname(sys.argv[0])) + '/'
# Set file paths # Get variables environment
dotenv_file = MY_PATH / ".env" if not os.path.isfile(MY_PATH + '.env'):
dotenv_template = MY_PATH / ".env.template" copyfile(MY_PATH + ".env.template",MY_PATH + ".env")
dotenv_path = join(dirname(__file__),MY_PATH + '.env')
load_dotenv(dotenv_path)
# Check and create dotenv file # Parse arguments
if not dotenv_file.is_file(): parser = argparse.ArgumentParser(description="Client CLI pour Cesium+ et Ḡchange")
dotenv_file.write_text(dotenv_template.read_text()) 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")
# Load environment variables subparsers = parser.add_subparsers(title="Commandes de jaklis", dest="cmd")
load_dotenv(dotenv_file) 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+")
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")
# Set global values (default parameters) regarding environment variables # Messages management
node = os.getenv("DUNITER") + "/gva" or "https://g1v1.p2p.legal/gva" read_cmd.add_argument('-n', '--number',type=int, default=3, help="Affiche les NUMBER derniers messages")
pod = os.getenv("ESNODE") or "https://g1.data.e-is.pro" read_cmd.add_argument('-j', '--json', action='store_true', help="Sort au format JSON")
destPubkey = False read_cmd.add_argument('-o', '--outbox', action='store_true', help="Lit les messages envoyés")
# define parser send_cmd.add_argument('-d', '--destinataire', required=True, help="Destinataire du message")
parser = argparse.ArgumentParser( send_cmd.add_argument('-t', '--titre', help="Titre du message à envoyer")
description="CLI Client for Cesium+ and Ḡchange", send_cmd.add_argument('-m', '--message', help="Message à envoyer")
epilog="current node: '" + node + "', current pod: '" + pod + "'.", 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")
# load global arguments delete_cmd.add_argument('-i', '--id', action='append', nargs='+', required=True, help="ID(s) du/des message(s) à supprimer")
parser.add_argument( delete_cmd.add_argument('-o', '--outbox', action='store_true', help="Suppression d'un message envoyé")
"-v",
"--version",
action="store_true",
help="Display the current program version",
)
parser.add_argument("-k", "--key", help="Path to the keyfile (PubSec)")
parser.add_argument(
"-n", "--node", help="Address of the Cesium+, Gchange, or Duniter node to use"
)
# Define commands with arguments # Profiles management
commands = { setProfile_cmd.add_argument('-n', '--name', help="Nom du profile")
"read": { setProfile_cmd.add_argument('-d', '--description', help="Description du profile")
"help": "Read messages", setProfile_cmd.add_argument('-v', '--ville', help="Ville du profile")
"arguments": { setProfile_cmd.add_argument('-a', '--adresse', help="Adresse du profile")
("n", "number"): { setProfile_cmd.add_argument('-pos', '--position', nargs=2, help="Points géographiques (lat + lon)")
"type": int, setProfile_cmd.add_argument('-s', '--site', help="Site web du profile")
"default": 3, setProfile_cmd.add_argument('-A', '--avatar', help="Chemin vers mon avatar en PNG")
"help": "Display the last NUMBER messages",
}, getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile")
("j", "json"): {"action": "store_true", "help": "Output in JSON format"}, getProfile_cmd.add_argument('-a', '--avatar', action='store_true', help="Récupérer également l'avatar au format raw base64")
("o", "outbox"): {"action": "store_true", "help": "Read sent messages"},
}, # Likes management
"type": "cesium", stars_cmd.add_argument('-p', '--profile', help="Profile cible")
}, stars_cmd.add_argument('-n', '--number', type=int, help="Nombre d'étoile")
"send": { unstars_cmd.add_argument('-p', '--profile', help="Profile à dénoter")
"help": "Send a message",
"arguments": { # Offers management
("d", "destinataire"): { getoffer_cmd.add_argument('-i', '--id', help="Annonce cible à récupérer")
"required": True, setoffer_cmd.add_argument('-t', '--title', help="Titre de l'annonce à créer")
"help": "Recipient of the message", 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")
("t", "titre"): {"help": "Title of the message to send"}, setoffer_cmd.add_argument('-l', '--localisation', nargs=2, help="Localisation de l'annonce à créer (lat + lon)")
("m", "message"): {"help": "Message to send"}, setoffer_cmd.add_argument('-p', '--picture', help="Image de l'annonce à créer")
("f", "fichier"): {"help": "Send the message from the 'FILE'"}, setoffer_cmd.add_argument('-ci', '--city', help="Ville de l'annonce à créer")
("o", "outbox"): { setoffer_cmd.add_argument('-pr', '--price', help="Prix de l'annonce à créer")
"action": "store_true", deleteoffer_cmd.add_argument('-i', '--id', help="Annonce cible à supprimer")
"help": "Send the message to the outbox",
}, # GVA usage
}, pay_cmd.add_argument('-p', '--pubkey', help="Destinataire du paiement")
"type": "cesium", 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")
"delete": { pay_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool")
"help": "Delete a message", pay_cmd.add_argument('-v', '--verbose', action='store_true', help="Affiche le résultat JSON de la transaction")
"arguments": {
("i", "id"): { history_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé")
"action": "append", history_cmd.add_argument('-n', '--number',type=int, default=10, help="Affiche les NUMBER dernières transactions")
"nargs": "+", history_cmd.add_argument('-j', '--json', action='store_true', help="Affiche le résultat en format JSON")
"required": True, history_cmd.add_argument('--nocolors', action='store_true', help="Affiche le résultat en noir et blanc")
"help": "ID(s) of the message(s) to delete",
}, balance_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé")
("o", "outbox"): { balance_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool")
"action": "store_true",
"help": "Delete a sent message",
},
},
"type": "cesium",
},
"get": {
"help": "View a Cesium+ profile",
"arguments": {
("p", "profile"): {"help": "Profile name"},
("a", "avatar"): {
"action": "store_true",
"help": "Also retrieve the avatar in raw base64 format",
},
},
"type": "cesium",
},
"page": {
"help": "View a Cesium+ page",
"arguments": {
("p", "page"): {"help": "Page name"},
("a", "avatar"): {
"action": "store_true",
"help": "Also retrieve the page's avatar in raw base64 format",
},
},
"type": "cesium",
},
"set": {
"help": "Configure your Cesium+ profile",
"arguments": {
("n", "name"): {"help": "Profile name"},
("d", "description"): {"help": "Profile description"},
("v", "ville"): {"help": "Profile city"},
("a", "adresse"): {"help": "Profile address"},
("pos", "position"): {
"nargs": 2,
"help": "Geographical coordinates (lat + lon)",
},
("s", "site"): {"help": "Profile website"},
("A", "avatar"): {"help": "Path to profile avatar in PNG"},
},
"type": "cesium",
},
"erase": {
"help": "Erase your Cesium+ profile",
"arguments": {},
"type": "cesium",
},
"stars": {
"help": "View a profile's stars / Rate a profile (option -s RATING)",
"arguments": {
("p", "profile"): {"help": "Target profile"},
("n", "number"): {"type": int, "help": "Number of stars"},
},
"type": "cesium",
},
"unstars": {
"help": "Remove a star",
"arguments": {
("p", "profile"): {"help": "Profile to unstar"},
},
"type": "cesium",
},
"getoffer": {
"help": "Get information about a Ḡchange listing",
"arguments": {
("i", "id"): {"help": "Target listing to retrieve"},
},
"type": "cesium",
},
"setoffer": {
"help": "Create a Ḡchange listing",
"arguments": {
("t", "title"): {"help": "Title of the listing to create"},
("d", "description"): {"help": "Description of the listing to create"},
("c", "category"): {"help": "Category of the listing to create"},
("l", "location"): {
"nargs": 2,
"help": "Location of the listing to create (lat + lon)",
},
("p", "picture"): {"help": "Image of the listing to create"},
("ci", "city"): {"help": "City of the listing to create"},
("pr", "price"): {"help": "Price of the listing to create"},
},
"type": "cesium",
},
"deleteoffer": {
"help": "Delete a Ḡchange listing",
"arguments": {
("i", "id"): {"help": "Target listing to delete"},
},
"type": "cesium",
},
"geolocProfiles": {
"help": "Get JSON of all geolocated accounts",
"arguments": {},
"type": "cesium",
},
"pay": {
"help": "Pay in Ḡ1",
"arguments": {
("p", "pubkey"): {"help": "Payment recipient"},
("a", "amount"): {"type": float, "help": "Transaction amount"},
("c", "comment"): {
"default": "",
"help": "Transaction comment",
"nargs": "*",
},
("m", "mempool"): {
"action": "store_true",
"help": "Use mempool sources",
},
("v", "verbose"): {
"action": "store_true",
"help": "Display the JSON result of the transaction",
},
},
"type": "gva",
},
"history": {
"help": "View Ḡ1 account transaction history",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
("n", "number"): {
"type": int,
"default": 10,
"help": "Display the last NUMBER transactions",
},
("j", "json"): {
"action": "store_true",
"help": "Display the result in JSON format",
},
("nocolors"): {
"action": "store_true",
"help": "Display the result in black and white",
},
},
"type": "gva",
},
"balance": {
"help": "View Ḡ1 account balance",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
("m", "mempool"): {
"action": "store_true",
"help": "Use mempool sources",
},
},
"type": "gva",
},
"id": {
"help": "View public key/username identity",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
("u", "username"): {"help": "Username of the target account"},
},
"type": "gva",
},
"idBalance": {
"help": "View public key/username identity and balance",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
},
"type": "gva",
},
"currentUd": {
"help": "Display the current Universal Dividend amount",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
},
"type": "gva",
},
"listWallets": {
"help": "List all G1 wallets",
"arguments": {
("m", "mbr"): {
"action": "store_true",
"help": "Display raw list of member pubkeys",
},
("nm", "non_mbr"): {
"action": "store_true",
"help": "Display raw list of nonmember identity pubkeys",
},
("l", "larf"): {
"action": "store_true",
"help": "Display raw list of nonmember pubkeys",
},
("b", "brut"): {
"action": "store_true",
"help": "Display raw list of all pubkeys",
},
},
"type": "gva",
},
}
# Process commands and arguments
subparsers = parser.add_subparsers(title="jaklis Commands", dest="cmd")
for cmd, cmd_info in commands.items():
cmd_parser = subparsers.add_parser(cmd, help=cmd_info["help"])
for args, kwargs in cmd_info["arguments"].items():
if isinstance(args, str):
cmd_parser.add_argument("--" + args, **kwargs)
else:
short_arg, long_arg = args
cmd_parser.add_argument("-" + short_arg, "--" + long_arg, **kwargs)
args = parser.parse_args() args = parser.parse_args()
args_dict = vars(args)
cmd = args.cmd cmd = args.cmd
if args.version: if args.version:
print(__version__) print(__version__)
sys.exit(0) sys.exit(0)
if not cmd: if not cmd:
parser.print_help() parser.print_help()
sys.exit(0) sys.exit(1)
def createTmpDunikey(): def createTmpDunikey():
# Generate a pseudo-random nonce # Generate pseudo-random nonce
nonce = "".join( nonce=[]
random.choice(string.ascii_letters + string.digits) for _ in range(32) for _ in range(32):
) nonce.append(random.choice(string.ascii_letters + string.digits))
nonce = ''.join(nonce)
keyPath = "/tmp/secret.dunikey-" + nonce keyPath = "/tmp/secret.dunikey-" + nonce
# Create a dummy key (replace with actual key creation logic) key = SigningKey.from_credentials(getpass.getpass("Identifiant: "), getpass.getpass("Mot de passe: "), None)
key = SigningKey.from_credentials(
"sgse547yhd54xv6541srdh", "sfdgwdrhpkxdawsbszqpof1sdg65xc", None
)
key.save_pubsec_file(keyPath) key.save_pubsec_file(keyPath)
return keyPath return keyPath
# Check if we need dunikey
try:
pubkey = args.pubkey
except:
pubkey = False
try:
profile = args.profile
except:
profile = False
def get_arg_value(args, arg): if cmd in ('history','balance','get') and (pubkey or profile):
noNeedDunikey = True
keyPath = False
try: try:
return getattr(args, arg) dunikey = args.pubkey
except AttributeError: except:
return False dunikey = args.profile
else:
noNeedDunikey = False
def get_dunikey(args):
if args.key: if args.key:
return args.key dunikey = args.key
dunikey = os.getenv("DUNIKEY") keyPath = False
if not dunikey: else:
keyPath = createTmpDunikey() dunikey = os.getenv('DUNIKEY')
dunikey = keyPath if not dunikey:
keyPath = createTmpDunikey()
dunikey = keyPath
else:
keyPath = False
if not os.path.isfile(dunikey): if not os.path.isfile(dunikey):
HOME = os.getenv("HOME") HOME = os.getenv("HOME")
dunikey = HOME + dunikey dunikey = HOME + dunikey
if not os.path.isfile(dunikey): if not os.path.isfile(dunikey):
sys.stderr.write("The keyfile {0} is not found.\n".format(dunikey)) sys.stderr.write('Le fichier de trousseau {0} est introuvable.\n'.format(dunikey))
sys.exit(1) sys.exit(1)
return dunikey
pubkey = get_arg_value(args, "pubkey") # Construct CesiumPlus object
profile = get_arg_value(args, "profile") if cmd in ("read","send","delete","set","get","erase","stars","unstars","getoffer","setoffer","deleteoffer"):
from lib.cesium import CesiumPlus
noNeedDunikey = cmd in ( if args.node:
"history", pod = args.node
"balance", else:
"page", pod = os.getenv('POD')
"id", if not pod:
"idBalance", pod="https://g1.data.le-sou.org"
"listWallets",
"geolocProfiles",
) and (pubkey or profile)
if noNeedDunikey: cesium = CesiumPlus(dunikey, pod, noNeedDunikey)
dunikey = pubkey if pubkey else profile
else:
dunikey = get_dunikey(args)
keyPath = False if dunikey else createTmpDunikey()
def handle_cesium_commands(args, cmd, cesium):
# Get args of the command
cmd_args = (
list(zip(*list(commands[cmd]["arguments"].keys())))[1]
if len(commands[cmd]["arguments"].keys()) > 0
else []
)
cmd_args_dict = {arg: args_dict[arg] for arg in cmd_args if arg in args_dict}
cmd_args_values = list(cmd_args_dict.values())
# Messaging # Messaging
if cmd == "read": if cmd == "read":
cesium.read(*cmd_args_values) cesium.read(args.number, args.outbox, args.json)
elif cmd == "send": elif cmd == "send":
if args.fichier: if args.fichier:
with open(args.fichier, "r") as f: with open(args.fichier, 'r') as f:
msgT = f.read() msgT = f.read()
titre = msgT.splitlines(True)[0].replace("\n", "") titre = msgT.splitlines(True)[0].replace('\n', '')
msg = "".join(msgT.splitlines(True)[1:]) msg = ''.join(msgT.splitlines(True)[1:])
if args.titre: if args.titre:
titre = args.titre titre = args.titre
msg = msgT msg = msgT
@ -397,8 +186,8 @@ def handle_cesium_commands(args, cmd, cesium):
titre = args.titre titre = args.titre
msg = args.message msg = args.message
else: else:
titre = input("Enter the message title: ") titre = input("Indiquez le titre du message: ")
msg = input("Enter the message content: ") msg = input("Indiquez le contenu du message: ")
cesium.send(titre, msg, args.destinataire, args.outbox) cesium.send(titre, msg, args.destinataire, args.outbox)
@ -407,15 +196,11 @@ def handle_cesium_commands(args, cmd, cesium):
# Profiles # Profiles
elif cmd == "set": elif cmd == "set":
cesium.set(**cmd_args_dict) cesium.set(args.name, args.description, args.ville, args.adresse, args.position, args.site, args.avatar)
elif cmd == "get": elif cmd == "get":
cesium.get(**cmd_args_dict) cesium.get(args.profile, args.avatar)
elif cmd == "page":
cesium.getPage(**cmd_args_dict)
elif cmd == "erase": elif cmd == "erase":
cesium.erase() cesium.erase()
elif cmd == "geolocProfiles":
cesium.geolocProfiles(node)
# Stars # Stars
elif cmd == "stars": elif cmd == "stars":
@ -430,61 +215,35 @@ def handle_cesium_commands(args, cmd, cesium):
elif cmd == "getoffer": elif cmd == "getoffer":
cesium.getOffer(args.id) cesium.getOffer(args.id)
elif cmd == "setoffer": elif cmd == "setoffer":
cesium.setOffer(**cmd_args_dict) cesium.setOffer(args.title, args.description, args.city, args.localisation, args.category, args.price, args.picture)
elif cmd == "deleteoffer": elif cmd == "deleteoffer":
cesium.deleteOffer(**cmd_args_dict) cesium.deleteOffer(args.id)
# Construct GVA object
elif cmd in ("pay","history","balance"):
from lib.gva import GvaApi
if args.node:
node = args.node
else: else:
raise ValueError(f"Unknown command: {cmd}") node = os.getenv('NODE')
if not node:
node="https://g1.librelois.fr/gva"
if args.pubkey:
destPubkey = args.pubkey
else:
destPubkey = False
def handle_gva_commands(args, cmd, gva): gva = GvaApi(dunikey, node, destPubkey, noNeedDunikey)
# Get args of the command
cmd_args = (
list(zip(*list(commands[cmd]["arguments"].keys())))[1]
if len(commands[cmd]["arguments"].keys()) > 0
else []
)
cmd_args_dict = {arg: args_dict[arg] for arg in cmd_args if arg in args_dict}
# cmd_args_values = list(cmd_args_dict.values())
if cmd == "pay": if cmd == "pay":
gva.pay(args.amount, args.comment, args.mempool, args.verbose) gva.pay(args.amount, args.comment, args.mempool, args.verbose)
elif cmd == "history": if cmd == "history":
gva.history(args.json, args.nocolors, args.number) gva.history(args.json, args.nocolors, args.number)
elif cmd == "balance": if cmd == "balance":
gva.balance(args.mempool) gva.balance(args.mempool)
elif cmd == "id":
gva.id(**cmd_args_dict)
elif cmd == "idBalance":
gva.idBalance(**cmd_args_dict)
elif cmd == "currentUd":
gva.currentUd()
elif cmd == "listWallets":
gva.listWallets(args.brut, args.mbr, args.non_mbr, args.larf)
else:
raise ValueError(f"Unknown command: {cmd}")
# Construct the CesiumPlus object
if commands[cmd]["type"] == "cesium":
if args.node:
pod = args.node
cesium = CesiumPlus(dunikey, pod, noNeedDunikey)
handle_cesium_commands(args, cmd, cesium)
# Construct the GvaApi object
elif commands[cmd]["type"] == "gva":
if args.node:
node = args.node
if hasattr(args, "pubkey"):
destPubkey = args.pubkey
gva = GvaApi(dunikey, node, destPubkey, noNeedDunikey)
handle_gva_commands(args, cmd, gva)
else:
raise ValueError(f"Unknown command: {cmd}")
if keyPath: if keyPath:
os.remove(keyPath) os.remove(keyPath)

View File

@ -1,19 +1,16 @@
import json
import re, string, random, base64 import re, string, random, base64
from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
from lib.geolocProfiles import GeolocProfiles
from lib.getPages import Pages
from lib.messaging import ReadFromCesium, SendToCesium, DeleteFromCesium from lib.messaging import ReadFromCesium, SendToCesium, DeleteFromCesium
from lib.profiles import Profiles from lib.profiles import Profiles
from lib.stars import ReadLikes, SendLikes, UnLikes from lib.likes import ReadLikes, SendLikes, UnLikes
from lib.offers import Offers from lib.offers import Offers
class CesiumPlus(CesiumCommon): class CesiumPlus(CesiumCommon):
#################### Messaging #################### #################### Messaging ####################
def read(self, nbrMsg, isJSON, outbox): def read(self, nbrMsg, outbox, isJSON):
readCesium = ReadFromCesium(self.dunikey, self.pod) readCesium = ReadFromCesium(self.dunikey, self.pod)
jsonMsg = readCesium.sendDocument(nbrMsg, outbox) jsonMsg = readCesium.sendDocument(nbrMsg, outbox)
if isJSON: if isJSON:
jsonFormat = readCesium.jsonMessages(jsonMsg, nbrMsg, outbox) jsonFormat = readCesium.jsonMessages(jsonMsg, nbrMsg, outbox)
@ -26,18 +23,16 @@ class CesiumPlus(CesiumCommon):
sendCesium.recipient = recipient sendCesium.recipient = recipient
# Generate pseudo-random nonce # Generate pseudo-random nonce
nonce = [] nonce=[]
for _ in range(32): for _ in range(32):
nonce.append(random.choice(string.ascii_letters + string.digits)) nonce.append(random.choice(string.ascii_letters + string.digits))
sendCesium.nonce = base64.b64decode("".join(nonce)) sendCesium.nonce = base64.b64decode(''.join(nonce))
finalDoc = sendCesium.configDoc( finalDoc = sendCesium.configDoc(sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg)) # Configure JSON document to send
sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg) sendCesium.sendDocument(finalDoc, outbox) # Send final signed document
) # Configure JSON document to send
sendCesium.sendDocument(finalDoc, outbox) # Send final signed document
def delete(self, idsMsgList, outbox): def delete(self, idsMsgList, outbox):
deleteCesium = DeleteFromCesium(self.dunikey, self.pod) deleteCesium = DeleteFromCesium(self.dunikey, self.pod)
# deleteCesium.issuer = recipient # deleteCesium.issuer = recipient
for idMsg in idsMsgList: for idMsg in idsMsgList:
finalDoc = deleteCesium.configDoc(idMsg, outbox) finalDoc = deleteCesium.configDoc(idMsg, outbox)
@ -45,74 +40,40 @@ class CesiumPlus(CesiumCommon):
#################### Profiles #################### #################### Profiles ####################
def set( def set(self, name=None, description=None, ville=None, adresse=None, position=None, site=None, avatar=None):
self, setProfile = Profiles(self.dunikey, self.pod)
name=None, document = setProfile.configDocSet(name, description, ville, adresse, position, site, avatar)
description=None, result = setProfile.sendDocument(document,'set')
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) print(result)
return result return result
def get(self, profile=None, avatar=None): def get(self, profile=None, avatar=None):
getProfile = Profiles(self.dunikey, self.pod, self.noNeedDunikey) getProfile = Profiles(self.dunikey, self.pod, self.noNeedDunikey)
if not profile: if not profile:
profile = self.pubkey profile = self.pubkey
if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45: if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45:
scope = "title" scope = 'title'
else: else:
scope = "_id" scope = '_id'
document = getProfile.configDocGet(profile, scope, avatar) document = getProfile.configDocGet(profile, scope, avatar)
resultJSON = getProfile.sendDocument(document, "get") resultJSON = getProfile.sendDocument(document, 'get')
result = getProfile.parseJSON(resultJSON) result = getProfile.parseJSON(resultJSON)
print(result) print(result)
def getPage(self, page=None, avatar=None):
getPage = Pages(self.dunikey, self.pod, self.noNeedDunikey)
if not page:
page = self.pubkey
if not re.match(PUBKEY_REGEX, page) or len(page) > 45:
scope = "title"
else:
scope = "_id"
document = getPage.configDocGet(page, scope, avatar)
resultJSON = getPage.sendDocument(document, "get")
result = getPage.parseJSON(resultJSON)
print(result)
def erase(self): def erase(self):
eraseProfile = Profiles(self.dunikey, self.pod) eraseProfile = Profiles(self.dunikey, self.pod)
document = eraseProfile.configDocErase() document = eraseProfile.configDocErase()
result = eraseProfile.sendDocument(document, "erase") result = eraseProfile.sendDocument(document,'erase')
print(result) print(result)
def geolocProfiles(self, node):
geolocProfiles = GeolocProfiles(self.dunikey, self.pod)
cesiumProfiles = geolocProfiles.getCesiumProfiles()
gvaProfiles = geolocProfiles.getGVAProfiles(node)
result = geolocProfiles.formatProfiles(cesiumProfiles, json.loads(gvaProfiles))
print(json.dumps(result))
#################### Likes #################### #################### Likes ####################
def readLikes(self, profile=False): def readLikes(self, profile=False):
likes = ReadLikes(self.dunikey, self.pod, self.noNeedDunikey) likes = ReadLikes(self.dunikey, self.pod, self.noNeedDunikey)
document = likes.configDoc(profile) document = likes.configDoc(profile)
result = likes.sendDocument(document) result = likes.sendDocument(document)
result = likes.parseResult(result) result = likes.parseResult(result)
@ -120,13 +81,13 @@ class CesiumPlus(CesiumCommon):
print(result) print(result)
def like(self, stars, profile=False): def like(self, stars, profile=False):
likes = SendLikes(self.dunikey, self.pod) likes = SendLikes(self.dunikey, self.pod)
document = likes.configDoc(profile, stars) document = likes.configDoc(profile, stars)
if document: if document:
likes.sendDocument(document, profile) likes.sendDocument(document, profile)
def unLike(self, pubkey, silent=False): def unLike(self, pubkey, silent=False):
likes = UnLikes(self.dunikey, self.pod) likes = UnLikes(self.dunikey, self.pod)
idLike = likes.checkLike(pubkey) idLike = likes.checkLike(pubkey)
if idLike: if idLike:
document = likes.configDoc(idLike) document = likes.configDoc(idLike)
@ -134,37 +95,25 @@ class CesiumPlus(CesiumCommon):
#################### Offer #################### #################### Offer ####################
def setOffer( def setOffer(self, title=None, description=None, city=None, localisation=None, category=None, price=None, picture=None):
self, setOffer = Offers(self.dunikey, self.pod)
title=None, document = setOffer.configDocSet(title, description, city, localisation, category, price, picture)
description=None, result = setOffer.sendDocumentSet(document,'set')
city=None,
location=None,
category=None,
price=None,
picture=None,
):
setOffer = Offers(self.dunikey, self.pod)
document = setOffer.configDocSet(
title, description, city, location, category, price, picture
)
result = setOffer.sendDocumentSet(document, "set")
# print(result) print(result)
return result return result
def getOffer(self, id, avatar=None): def getOffer(self, id, avatar=None):
getOffer = Offers(self.dunikey, self.pod, self.noNeedDunikey) getOffer = Offers(self.dunikey, self.pod, self.noNeedDunikey)
resultJSON = getOffer.sendDocumentGet(id, "get") resultJSON = getOffer.sendDocumentGet(id, 'get')
# print(resultJSON)
result = getOffer.parseJSON(resultJSON) result = getOffer.parseJSON(resultJSON)
print(result) print(result)
def deleteOffer(self, id): def deleteOffer(self, id):
eraseOffer = Offers(self.dunikey, self.pod) eraseOffer = Offers(self.dunikey, self.pod)
document = eraseOffer.configDocErase(id) document = eraseOffer.configDocErase(id)
result = eraseOffer.sendDocumentSet(document, "delete", id) result = eraseOffer.sendDocumentSet(document,'delete', id)
print(result) print(result)

View File

@ -1,40 +0,0 @@
#!/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
class currentUd:
def __init__(self, node):
# Define Duniter GVA node
transport = AIOHTTPTransport(url=node)
self.client = Client(transport=transport, fetch_schema_from_transport=True)
def sendDoc(self):
# Build UD generation document
queryBuild = gql(
"""
query {
currentUd {
amount
}
}
"""
)
paramsBuild = {
}
# Send UD document
try:
udValue = 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 DU:\n" + message + "\n")
sys.exit(1)
udValueFinal = udValue['currentUd']['amount']
return udValueFinal

View File

@ -1,112 +0,0 @@
import requests
from time import time
from lib.cesiumCommon import CesiumCommon
from lib.gvaWallets import ListWallets
class GeolocProfiles(CesiumCommon):
def getCesiumProfiles(self):
# Send a POST request to the Cesium profiles API
response = requests.post(
"https://g1.data.e-is.pro/user/profile/_search?scroll=2m",
json={
"query": {
"constant_score": {
"filter": [
{"exists": {"field": "geoPoint"}},
{
"geo_bounding_box": {
"geoPoint": {
"top_left": {"lat": 90, "lon": -180},
"bottom_right": {"lat": -90, "lon": 180},
}
}
},
]
}
},
"_source": [
"title",
"avatar._content_type",
"description",
"city",
"address",
"socials.url",
"creationTime",
"membersCount",
"type",
"geoPoint",
],
"size": 20000,
},
)
scroll_id = response.json()["_scroll_id"]
finalResult: dict | None = response.json()["hits"]["hits"]
while True:
# Send a scroll request to get the next page
response_scroll = requests.post(
"https://g1.data.e-is.pro/_search/scroll",
json={"scroll_id": scroll_id, "scroll": "2m"},
)
# Check if the response is empty (no results) or if there's an error
if (
not response_scroll.json()["hits"]["hits"]
or "error" in response_scroll.json()
):
break
else:
finalResult.extend(response_scroll.json()["hits"]["hits"])
# Process the results here
# Delete the scroll context when done
requests.delete(
"https://g1.data.e-is.pro/_search/scroll", json={"scroll_id": [scroll_id]}
)
return finalResult
def getGVAProfiles(self, node):
# Retrieve GVA profiles using the ListWallets class
gva = ListWallets(node, map=True)
return gva.sendDoc()
def formatProfiles(self, cesiumProfiles, gvaProfiles):
walletsResult = []
for profile in cesiumProfiles:
source: dict = profile["_source"]
pubkey: dict = profile["_id"]
if pubkey not in gvaProfiles:
continue
# Extract necessary information from the profiles
id_info: dict = gvaProfiles[pubkey].get("id") or {}
isMember = id_info.get("isMember", False)
userId = id_info.get("username")
title = source.get("title")
city = source.get("city")
avatar = source.get("avatar")
socials = source.get("socials")
description = source.get("description")
address = source.get("address")
walletsResult.append(
{
"pubkey": pubkey,
**({"address": address} if address else {}),
**({"city": city} if city else {}),
**({"description": description} if description else {}),
**({"avatar": avatar} if avatar else {}),
**({"userId": userId} if userId else {}),
"isMember": isMember,
"geoPoint": source["geoPoint"],
**({"socials": socials} if socials else {}),
**({"title": title} if title else {}),
}
)
return {"wallets": walletsResult, "time": int(time())}

View File

@ -1,125 +0,0 @@
import sys, re, json, requests, base64
from time import time
from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
class Pages(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","geoPoint"],
"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'

View File

@ -1,11 +1,8 @@
from lib.currentUd import currentUd
from lib.gvaWallets import ListWallets
import sys, re import sys, re
from lib.natools import get_privkey from lib.natools import get_privkey
from lib.gvaPay import Transaction, PUBKEY_REGEX from lib.gvaPay import Transaction, PUBKEY_REGEX
from lib.gvaHistory import History from lib.gvaHistory import History
from lib.gvaBalance import Balance from lib.gvaBalance import Balance
from lib.gvaID import Id
class GvaApi(): class GvaApi():
def __init__(self, dunikey, node, pubkey, noNeedDunikey=False): def __init__(self, dunikey, node, pubkey, noNeedDunikey=False):
@ -39,7 +36,6 @@ class GvaApi():
#################### Payments #################### #################### Payments ####################
def pay(self, amount, comment, mempool, verbose): def pay(self, amount, comment, mempool, verbose):
comment = " ".join(comment)
gva = Transaction(self.dunikey, self.node, self.destPubkey, amount, comment, mempool, verbose) gva = Transaction(self.dunikey, self.node, self.destPubkey, amount, comment, mempool, verbose)
gva.genDoc() gva.genDoc()
gva.checkTXDoc() gva.checkTXDoc()
@ -61,24 +57,3 @@ class GvaApi():
gva = Balance(self.dunikey, self.node, self.destPubkey, useMempool) gva = Balance(self.dunikey, self.node, self.destPubkey, useMempool)
balanceValue = gva.sendDoc() balanceValue = gva.sendDoc()
print(balanceValue) print(balanceValue)
def id(self, pubkey, username):
gva = Id(self.dunikey, self.node, pubkey, username)
result = gva.sendDoc()
print(result)
def idBalance(self, pubkey):
gva = Id(self.dunikey, self.node, pubkey)
result = gva.sendDoc(True)
print(result)
def currentUd(self):
gva = currentUd(self.node)
result = gva.sendDoc()
print(result)
def listWallets(self, brut, brutMbr, brutNonMbr, brutLarf):
gva = ListWallets(self.node, brut, brutMbr, brutNonMbr, brutLarf)
result = gva.sendDoc()
print(result)

View File

@ -26,7 +26,7 @@ class Balance:
# Build balance generation document # Build balance generation document
queryBuild = gql( queryBuild = gql(
""" """
query ($pubkey: PkOrScriptGva!){ query ($pubkey: String!){
balance(script: $pubkey) { balance(script: $pubkey) {
amount amount
} }
@ -44,10 +44,7 @@ class Balance:
message = ast.literal_eval(str(e))["message"] message = ast.literal_eval(str(e))["message"]
sys.stderr.write("Echec de récupération du solde:\n" + message + "\n") sys.stderr.write("Echec de récupération du solde:\n" + message + "\n")
sys.exit(1) sys.exit(1)
if (balanceResult['balance'] == None): balanceValue = 'null'
else:
balanceValue = balanceResult['balance']['amount']/100
balanceValue = balanceResult['balance']['amount']/100
# print(balanceValue) # print(balanceValue)
return balanceValue return balanceValue

View File

@ -1,8 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys, re, os.path, json, ast, time, hashlib import sys, re, os.path, json, ast, time
from datetime import datetime from datetime import datetime
from duniterpy.key import base58
from termcolor import colored from termcolor import colored
from lib.natools import fmt, sign, get_privkey from lib.natools import fmt, sign, get_privkey
from gql import gql, Client from gql import gql, Client
@ -28,9 +27,9 @@ class History:
# Build history generation document # Build history generation document
queryBuild = gql( queryBuild = gql(
""" """
query ($pubkey: PubKeyGva!, $script: PkOrScriptGva!, $number: Int!){ query ($pubkey: String!, $number: Int!){
txsHistoryBc( txsHistoryBc(
script: $script pubkeyOrScript: $pubkey
pagination: { pageSize: $number, ord: DESC } pagination: { pageSize: $number, ord: DESC }
) { ) {
both { both {
@ -43,11 +42,9 @@ class History:
node { node {
currency currency
issuers issuers
blockstamp
outputs outputs
comment comment
writtenTime writtenTime
hash
} }
} }
} }
@ -58,21 +55,17 @@ class History:
issuers issuers
comment comment
outputs outputs
receivedTime writtenTime
blockstamp
hash
} }
sending { receiving {
currency currency
issuers issuers
comment comment
outputs outputs
receivedTime writtenTime
blockstamp
hash
} }
} }
balance(script: $script) { balance(script: $pubkey) {
amount amount
base base
} }
@ -90,8 +83,7 @@ class History:
) )
paramsBuild = { paramsBuild = {
"pubkey": self.pubkey, "pubkey": self.pubkey,
"number": number, "number": number
"script": f"SIG({self.pubkey})",
} }
# Send history document # Send history document
@ -125,7 +117,6 @@ class History:
trans[i] = [] trans[i] = []
trans[i].append(direction) trans[i].append(direction)
trans[i].append(transaction['writtenTime']) trans[i].append(transaction['writtenTime'])
if direction == 'SENT': if direction == 'SENT':
trans[i].append(outPubkey) trans[i].append(outPubkey)
amount = int('-' + output.split(':')[0]) amount = int('-' + output.split(':')[0])
@ -140,12 +131,12 @@ class History:
trans[i].append(round(amount/self.UD, 2)) trans[i].append(round(amount/self.UD, 2))
trans[i].append(transaction['comment']) trans[i].append(transaction['comment'])
trans[i].append(base) trans[i].append(base)
trans[i].append(transaction['blockstamp'])
trans[i].append(transaction['hash'])
i += 1 i += 1
# Parse transactions in mempool # Parse transactions in mempool
for direction, resBc in self.historyDoc['txsHistoryMp'].items(): for direction in self.historyDoc['txsHistoryMp']:
resBc = []
resBc = self.historyDoc['txsHistoryMp'][direction]
for j, transaction in enumerate(resBc): for j, transaction in enumerate(resBc):
# print(transaction) # print(transaction)
transaction = resBc[j] transaction = resBc[j]
@ -154,11 +145,9 @@ class History:
# if direction == 'RECEIVING' or self.pubkey != outPubkey: # if direction == 'RECEIVING' or self.pubkey != outPubkey:
trans.append(i) trans.append(i)
trans[i] = [] trans[i] = []
trans[i].append(direction.upper()) trans[i].append(direction)
trans[i].append(transaction['receivedTime']) trans[i].append(int(time.time()))
if direction == 'SENDING':
# trans[i].append(int(time.time()))
if direction.upper() == 'SENDING':
trans[i].append(outPubkey) trans[i].append(outPubkey)
amount = int('-' + output.split(':')[0]) amount = int('-' + output.split(':')[0])
else: else:
@ -172,8 +161,6 @@ class History:
trans[i].append(round(amount/self.UD, 2)) trans[i].append(round(amount/self.UD, 2))
trans[i].append(transaction['comment']) trans[i].append(transaction['comment'])
trans[i].append(base) trans[i].append(base)
trans[i].append(transaction['blockstamp'])
trans[i].append(transaction['hash'])
i += 1 i += 1
# Order transactions by date # Order transactions by date
@ -189,12 +176,8 @@ class History:
def printHistory(self, trans, noColors): def printHistory(self, trans, noColors):
# Get balance # Get balance
if (self.historyDoc['balance'] == None): balance = self.historyDoc['balance']['amount']/100
balance = balanceUD = 'null' balanceUD = round(balance/self.UD, 2)
else:
balance = self.historyDoc['balance']['amount']/100
balanceUD = round(balance/self.UD, 2)
# Get currency # Get currency
currency = self.historyDoc['node']['peer']['currency'] currency = self.historyDoc['node']['peer']['currency']
@ -237,12 +220,11 @@ class History:
print('|', end='') print('|', end='')
print('-'.center(rows-1, '-')) print('-'.center(rows-1, '-'))
print('|', end='') print('|', end='')
checksum = self.gen_checksum(t[2]) printKey = t[2][0:8] + '\u2026' + t[2][-3:]
shortPubkey = t[2][0:4] + '\u2026' + t[2][-4:] + ':' + checksum
if noColors: if noColors:
print(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, shortPubkey, t[3], t[4], comment)) print(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment))
else: else:
print(colored(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, shortPubkey, t[3], t[4], comment), color)) print(colored(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment), color))
print('|', end='') print('|', end='')
print('-'.center(rows-1, '-')) print('-'.center(rows-1, '-'))
print('|', end='') print('|', end='')
@ -253,29 +235,17 @@ class History:
print(colored('Reçus', 'green'), '-', colored('En cours de réception', 'yellow'), '-', colored('Envoyé', 'blue'), '-', colored("En cours d'envoi", 'red')) print(colored('Reçus', 'green'), '-', colored('En cours de réception', 'yellow'), '-', colored('Envoyé', 'blue'), '-', colored("En cours d'envoi", 'red'))
return trans return trans
def gen_checksum(self, pubkey):
"""
Returns the checksum of the input pubkey (encoded in b58)
thx Matograine
"""
pubkey_byte = base58.Base58Encoder.decode(str.encode(pubkey))
hash = hashlib.sha256(hashlib.sha256(pubkey_byte).digest()).digest()
return base58.Base58Encoder.encode(hash)[:3]
def jsonHistory(self, transList): def jsonHistory(self, transList):
dailyJSON = [] dailyJSON = []
for i, trans in enumerate(transList): for i, trans in enumerate(transList):
dailyJSON.append(i) dailyJSON.append(i)
dailyJSON[i] = {} dailyJSON[i] = {}
dailyJSON[i]['status'] = trans[0].upper()
dailyJSON[i]['date'] = trans[1] dailyJSON[i]['date'] = trans[1]
dailyJSON[i]['pubkey'] = trans[2] dailyJSON[i]['pubkey'] = trans[2]
dailyJSON[i]['amount'] = trans[3] dailyJSON[i]['amount'] = trans[3]
dailyJSON[i]['amountUD'] = trans[4] dailyJSON[i]['amountUD'] = trans[4]
dailyJSON[i]['comment'] = trans[5] dailyJSON[i]['comment'] = trans[5]
dailyJSON[i]['blockstamp'] = trans[7]
dailyJSON[i]['hash'] = trans[8]
dailyJSON = json.dumps(dailyJSON, indent=2) dailyJSON = json.dumps(dailyJSON, indent=2)
# If we want to write JSON to a file # If we want to write JSON to a file

View File

@ -1,81 +0,0 @@
#!/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 Id:
def __init__(self, dunikey, node, pubkey='', username=''):
self.dunikey = dunikey
self.pubkey = pubkey if pubkey else get_privkey(dunikey, "pubsec").pubkey
self.username = username
# 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, getBalance=False):
# Build balance generation document
if (getBalance):
queryBuild = gql(
"""
query ($pubkey: PubKeyGva!, $script: PkOrScriptGva!){
idty (pubkey: $pubkey) {
isMember
username
}
balance(script: $script) {
amount
}
}
"""
)
else:
queryBuild = gql(
"""
query ($pubkey: PubKeyGva!){
idty (pubkey: $pubkey) {
isMember
username
}
}
"""
)
paramsBuild = {
"pubkey": self.pubkey,
"script": f"SIG({self.pubkey})"
}
# Send balance document
try:
queryResult = self.client.execute(queryBuild, variable_values=paramsBuild)
except Exception as e:
sys.stderr.write("Echec de récupération du solde:\n" + str(e) + "\n")
sys.exit(1)
jsonBrut = queryResult
if (getBalance):
if (queryResult['balance'] == None):
jsonBrut['balance'] = {"amount": 0.0}
else:
jsonBrut['balance'] = queryResult['balance']['amount']/100
if (queryResult['idty'] == None):
username = 'Matiou'
isMember = False
else:
username = queryResult['idty']['username']
isMember = queryResult['idty']['isMember']
return json.dumps(jsonBrut, indent=2)

View File

@ -6,7 +6,7 @@ from lib.natools import fmt, sign, get_privkey
from gql import gql, Client from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport from gql.transport.aiohttp import AIOHTTPTransport
PUBKEY_REGEX = "(?![OIl])[0-9A-Za-z]{42,45}" PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}"
class Transaction: class Transaction:
@ -46,7 +46,7 @@ class Transaction:
if self.verbose: print("useMempool:", str(self.useMempool)) if self.verbose: print("useMempool:", str(self.useMempool))
queryBuild = gql( queryBuild = gql(
""" """
query ($recipient: PkOrScriptGva!, $issuer: PubKeyGva!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx( query ($recipient: String!, $issuer: String!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx(
amount: $amount amount: $amount
comment: $comment comment: $comment
issuer: $issuer issuer: $issuer

View File

@ -1,100 +0,0 @@
#!/usr/bin/env python3
import sys
import json
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
from lib.natools import fmt, sign, get_privkey
class ListWallets:
def __init__(
self, node=False, brut=False, mbr=False, nonMbr=False, larf=False, map=False
):
# Initialize the ListWallets class with optional filters
self.mbr = mbr # Filter for members
self.larf = larf # Filter for non-empty identities
self.nonMbr = nonMbr # Filter for non-members
self.brut = brut # Output format flag (brut or JSON)
self.map = map # Output format flag (map or list)
# Define Duniter GVA node
transport = AIOHTTPTransport(url=node)
self.client = Client(transport=transport, fetch_schema_from_transport=True)
def sendDoc(self):
# Define the GraphQL query to retrieve wallet information
queryBuild = gql(
"""
{
wallets(pagination: { cursor: null, ord: ASC, pageSize: 0 }) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
script
balance {
amount
base
}
idty {
isMember
username
}
}
}
}
}
"""
)
try:
# Execute the GraphQL query
queryResult = self.client.execute(queryBuild)
except Exception as e:
# Handle any exceptions that occur during the query
sys.stderr.write("Failed to retrieve the list:\n" + str(e) + "\n")
sys.exit(1)
jsonBrut = queryResult["wallets"]["edges"]
walletList = []
walletMap = {}
for i, trans in enumerate(jsonBrut):
dataWork = trans["node"]
identity = dataWork["idty"]
is_member = identity and identity["isMember"]
# Apply filters based on member status and larf flag
member_filter = self.mbr and not is_member
non_member_filter = self.nonMbr and is_member
larf_filter = self.larf and identity
if member_filter or non_member_filter or larf_filter:
continue
wallet_data = {
"pubkey": dataWork["script"],
"balance": dataWork["balance"]["amount"] / 100,
"id": identity,
}
if self.map:
walletMap[dataWork["script"]] = wallet_data
else:
walletList.append(wallet_data)
if self.brut:
# Generate a list of formatted wallet names using list comprehension
names = [
wallet["pubkey"]
if not (self.mbr or self.nonMbr) or wallet["id"] is None
else f'{wallet["pubkey"]} {wallet["id"]["username"]}'
for wallet in walletList
]
return "\n".join(names)
else:
# Return JSON data in either map or list format
return json.dumps(walletMap if self.map else walletList, indent=2)

View File

@ -46,7 +46,6 @@ class ReadFromCesium(CesiumCommon):
# Parse JSON result and display messages # Parse JSON result and display messages
def readMessages(self, msgJSON, nbrMsg, outbox): def readMessages(self, msgJSON, nbrMsg, outbox):
def decrypt(msg): def decrypt(msg):
if msg is None: return ''
msg64 = base64.b64decode(msg) msg64 = base64.b64decode(msg)
return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode()
@ -68,10 +67,7 @@ class ReadFromCesium(CesiumCommon):
msgSrc = hits["_source"] msgSrc = hits["_source"]
self.issuer = msgSrc["issuer"] self.issuer = msgSrc["issuer"]
nonce = msgSrc["nonce"] nonce = msgSrc["nonce"]
try: nonce = base58.b58decode(nonce)
nonce = base58.b58decode(nonce)
except:
nonce = base58.b58decode('5aZdSqKGHBqm2uMPwN6XnfiiJKRieb1Hh')
self.dateS = msgSrc["time"] self.dateS = msgSrc["time"]
date = datetime.fromtimestamp(self.dateS).strftime(", le %d/%m/%Y à %H:%M ") date = datetime.fromtimestamp(self.dateS).strftime(", le %d/%m/%Y à %H:%M ")
if outbox: if outbox:
@ -98,7 +94,6 @@ class ReadFromCesium(CesiumCommon):
# Parse JSON result and display messages # Parse JSON result and display messages
def jsonMessages(self, msgJSON, nbrMsg, outbox): def jsonMessages(self, msgJSON, nbrMsg, outbox):
def decrypt(msg): def decrypt(msg):
if msg is None: return ''
msg64 = base64.b64decode(msg) msg64 = base64.b64decode(msg)
return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode()
@ -118,10 +113,7 @@ class ReadFromCesium(CesiumCommon):
msgSrc = hits["_source"] msgSrc = hits["_source"]
self.issuer = msgSrc["issuer"] self.issuer = msgSrc["issuer"]
nonce = msgSrc["nonce"] nonce = msgSrc["nonce"]
try: nonce = base58.b58decode(nonce)
nonce = base58.b58decode(nonce)
except:
nonce = base58.b58decode('5aZdSqKGHBqm2uMPwN6XnfiiJKRieb1Hh')
self.date = msgSrc["time"] self.date = msgSrc["time"]
if outbox: if outbox:
@ -131,18 +123,11 @@ class ReadFromCesium(CesiumCommon):
try: try:
self.title = decrypt(msgSrc["title"]) self.title = decrypt(msgSrc["title"])
except Exception as e:
self.title = "jaklis can't read that mother fucker"
# sys.stderr.write(colored(str('Error decrypt message title: ' + str(e)), 'red') + '\n')
# pp_json(hits)
try:
self.content = decrypt(msgSrc["content"]) self.content = decrypt(msgSrc["content"])
except Exception as e: except Exception as e:
self.content = "jaklis can't read that mother fucker" sys.stderr.write(colored(str(e), 'red') + '\n')
# sys.stderr.write(colored(str('Error decrypt message content: ' + str(e)), 'red') + '\n') pp_json(hits)
# pp_json(hits) continue
data.append(i) data.append(i)
data[i] = {} data[i] = {}
@ -151,7 +136,6 @@ class ReadFromCesium(CesiumCommon):
data[i]['pubkey'] = pubkey data[i]['pubkey'] = pubkey
data[i]['title'] = self.title data[i]['title'] = self.title
data[i]['content'] = self.content data[i]['content'] = self.content
# print('toto')
data = json.dumps(data, indent=2) data = json.dumps(data, indent=2)
return data return data

23
lib/offers.py Executable file → Normal file
View File

@ -4,7 +4,7 @@ from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
class Offers(CesiumCommon): class Offers(CesiumCommon):
# Configure JSON document SET to send # Configure JSON document SET to send
def configDocSet(self, title, description, city, location, category, price: float, picture): def configDocSet(self, title, description, city, localisation, category, price: float, picture):
timeSent = int(time()) timeSent = int(time())
# {"parent":"cat90","localizedNames":{"en":"Fruits &amp; Vegetables","es-ES":"Frutas y Vegetales","fr-FR":"Fruits &amp; Légumes"},"name":"Fruits &amp; Légumes","id":"cat92"} # {"parent":"cat90","localizedNames":{"en":"Fruits &amp; Vegetables","es-ES":"Frutas y Vegetales","fr-FR":"Fruits &amp; Légumes"},"name":"Fruits &amp; Légumes","id":"cat92"}
@ -13,20 +13,20 @@ class Offers(CesiumCommon):
if title: data['title'] = title if title: data['title'] = title
if description: data['description'] = description if description: data['description'] = description
if city: data['city'] = city if city: data['city'] = city
if location: if localisation:
geoPoint = {} geoPoint = {}
geoPoint['lat'] = location[0] geoPoint['lat'] = localisation[0]
geoPoint['lon'] = location[1] geoPoint['lon'] = localisation[1]
data['geoPoint'] = geoPoint data['geoPoint'] = geoPoint
if picture: if picture:
picture = open(picture, 'rb').read() picture = open(picture, 'rb').read()
picture = base64.b64encode(picture).decode() picture = base64.b64encode(picture).decode()
data['thumbnail'] = {} data['picture'] = {}
data['thumbnail']['_content'] = picture data['picture']['_content'] = picture
data['thumbnail']['_content_type'] = "image/png" data['picture']['_content_type'] = "image/png"
# if category: data['category'] = category # if category: data['category'] = category
# else: # else:
data['category'] = {"parent":"cat24","localizedNames":{"en":"DVD / Films","es-ES":"DVDs / Cine","fr-FR":"DVD / Films"},"name":"DVD / Films","id":"cat25"} data['category'] = {"parent":"cat90","localizedNames":{"en":"Fruits &amp; Vegetables","es-ES":"Frutas y Vegetales","fr-FR":"Fruits &amp; Légumes"},"name":"Fruits &amp; Légumes","id":"cat92"}
if price: data['price'] = float(price) * 100 if price: data['price'] = float(price) * 100
data['type'] = 'offer' data['type'] = 'offer'
data['time'] = timeSent data['time'] = timeSent
@ -38,8 +38,7 @@ class Offers(CesiumCommon):
data['unit'] = None data['unit'] = None
data['fees'] = None data['fees'] = None
data['feesCurrency'] = None data['feesCurrency'] = None
if picture: data['picturesCount'] = 1 data['picturesCount'] = 0
else: data['picturesCount'] = 0
data['stock'] = 1 data['stock'] = 1
data['tags'] = [] data['tags'] = []
@ -94,13 +93,11 @@ class Offers(CesiumCommon):
if type == 'set': if type == 'set':
reqQuery = '{0}/market/record'.format(self.pod) reqQuery = '{0}/market/record'.format(self.pod)
elif type == 'get': elif type == 'get':
reqQuery = '{0}/market/record/{1}?_source=category,title,description,issuer,time,creationTime,location,address,city,price,unit,currency,thumbnail._content_type,thumbnail._content,picturesCount,type,stock,fees,feesCurrency,geoPoint,pubkey,freePrice'.format(self.pod, id) reqQuery = '{0}/market/record/{1}?_source=category,title,description,issuer,time,creationTime,location,address,city,price,unit,currency,thumbnail._content_type,picturesCount,type,stock,fees,feesCurrency,geoPoint,pubkey,freePrice'.format(self.pod, id)
elif type == 'erase': elif type == 'erase':
reqQuery = '{0}/market/delete'.format(self.pod) reqQuery = '{0}/market/delete'.format(self.pod)
result = requests.get(reqQuery, headers=headers) result = requests.get(reqQuery, headers=headers)
# print(result)
if result.status_code == 200: if result.status_code == 200:
# print(result.text) # print(result.text)
return result.text return result.text

View File

@ -1,4 +1,4 @@
import sys, json, requests, base64 import sys, re, json, requests, base64
from time import time from time import time
from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
@ -9,74 +9,71 @@ class Profiles(CesiumCommon):
timeSent = int(time()) timeSent = int(time())
data = {} data = {}
if name: if name: data['title'] = name
data["title"] = name if description: data['description'] = description
if description: if address: data['address'] = address
data["description"] = description if city: data['city'] = city
if address: if pos:
data["address"] = address
if city:
data["city"] = city
if pos:
geoPoint = {} geoPoint = {}
geoPoint["lat"] = pos[0] geoPoint['lat'] = pos[0]
geoPoint["lon"] = pos[1] geoPoint['lon'] = pos[1]
data["geoPoint"] = geoPoint data['geoPoint'] = geoPoint
if socials: if socials:
data["socials"] = [] data['socials'] = []
data["socials"].append({}) data['socials'].append({})
data["socials"][0]["type"] = "web" data['socials'][0]['type'] = "web"
data["socials"][0]["url"] = socials data['socials'][0]['url'] = socials
if avatar: if avatar:
avatar = open(avatar, "rb").read() avatar = open(avatar, 'rb').read()
avatar = base64.b64encode(avatar).decode() avatar = base64.b64encode(avatar).decode()
data["avatar"] = {} data['avatar'] = {}
data["avatar"]["_content"] = avatar data['avatar']['_content'] = avatar
data["avatar"]["_content_type"] = "image/png" data['avatar']['_content_type'] = "image/png"
data["time"] = timeSent data['time'] = timeSent
data["issuer"] = self.pubkey data['issuer'] = self.pubkey
data["version"] = 2 data['version'] = 2
data["tags"] = [] data['tags'] = []
document = json.dumps(data) document = json.dumps(data)
return self.signDoc(document) return self.signDoc(document)
# Configure JSON document GET to send # Configure JSON document GET to send
def configDocGet(self, profile, scope="title", getAvatar=None): def configDocGet(self, profile, scope='title', getAvatar=None):
if getAvatar: if getAvatar:
avatar = "avatar" avatar = "avatar"
else: else:
avatar = "avatar._content_type" avatar = "avatar._content_type"
data = { data = {
"query": { "query": {
"bool": { "bool": {
"should": [ "should":[
{"match": {scope: {"query": profile, "boost": 2}}}, {
{"prefix": {scope: profile}}, "match":{
scope:{
"query": profile,"boost":2
}
}
},{
"prefix": {scope: profile}
}
] ]
} }
}, },"highlight": {
"highlight": {"fields": {"title": {}, "tags": {}}}, "fields": {
"from": 0, "title":{},
"size": 100, "tags":{}
"_source": [ }
"title", },"from":0,
avatar, "size":100,
"description", "_source":["title", avatar,"description","city","address","socials.url","creationTime","membersCount","type"],
"city", "indices_boost":{"user":100,"page":1,"group":0.01
"address", }
"socials.url",
"creationTime",
"membersCount",
"type",
"geoPoint",
],
"indices_boost": {"user": 100, "page": 1, "group": 0.01},
} }
document = json.dumps(data) document = json.dumps(data)
return document return document
@ -85,45 +82,44 @@ class Profiles(CesiumCommon):
timeSent = int(time()) timeSent = int(time())
data = {} data = {}
data["time"] = timeSent data['time'] = timeSent
data["id"] = self.pubkey data['id'] = self.pubkey
data["issuer"] = self.pubkey data['issuer'] = self.pubkey
data["version"] = 2 data['version'] = 2
data["index"] = "user" data['index'] = "user"
data["type"] = "profile" data['type'] = "profile"
document = json.dumps(data) document = json.dumps(data)
return self.signDoc(document) return self.signDoc(document)
def sendDocument(self, document, type): def sendDocument(self, document, type):
headers = { headers = {
"Content-type": "application/json", 'Content-type': 'application/json',
} }
# Send JSON document and get JSON result # Send JSON document and get JSON result
if type == "set": if type == 'set':
reqQuery = "{0}/user/profile?pubkey={1}/_update?pubkey={1}".format( reqQuery = '{0}/user/profile?pubkey={1}/_update?pubkey={1}'.format(self.pod, self.pubkey)
self.pod, self.pubkey elif type == 'get':
) reqQuery = '{0}/user,page,group/profile,record/_search'.format(self.pod)
elif type == "get": elif type == 'erase':
reqQuery = "{0}/user,page,group/profile,record/_search".format(self.pod) reqQuery = '{0}/history/delete'.format(self.pod)
elif type == "erase":
reqQuery = "{0}/history/delete".format(self.pod)
result = requests.post(reqQuery, headers=headers, data=document) result = requests.post(reqQuery, headers=headers, data=document)
if result.status_code == 200: if result.status_code == 200:
# print(result.text) # print(result.text)
return result.text return result.text
else: else:
sys.stderr.write("Echec de l'envoi du document...\n" + result.text + "\n") sys.stderr.write("Echec de l'envoi du document...\n" + result.text + '\n')
def parseJSON(self, doc): def parseJSON(self, doc):
doc = json.loads(doc)["hits"]["hits"] doc = json.loads(doc)['hits']['hits']
if doc: if doc:
pubkey = {"pubkey": doc[0]["_id"]} pubkey = { "pubkey": doc[0]['_id'] }
rest = doc[0]["_source"] rest = doc[0]['_source']
final = {**pubkey, **rest} final = {**pubkey, **rest}
return json.dumps(final, indent=2) return json.dumps(final, indent=2)
else: else:
return "Profile vide" return 'Profile vide'

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
from io import BytesIO
import base64, base58, varint, os, json
# from lib.cesium import CesiumPlus as cs
## BytesIO adds a stream interface to bytes
## Exemple:
qr = BytesIO(bytes.fromhex("8316140212c28e52e034ecaf684fa3e5d755db519074f27ad086bddffd26b386e55f3b623ca01f0177c0f8ce5f6a69764c7bc10263ec"))
## Read from a file:
# qr = open("qrcode-AXfA-M5faml2THvBAmPs.bin","rb")
# qr = BytesIO(qr.read())
## Check magic number
assert qr.read(3) == b"\x83\x16\x14"
## Read data type
data_type = varint.decode_stream(qr)
## Read price type
raw_price_type = varint.decode_stream(qr)
price_type = raw_price_type >> 4
amount_len = raw_price_type & 0b1111
## Read pubkey
pubkey = qr.read(32)
pubkey_b58 = base58.b58encode(pubkey)
# print("Pubkey: {}".format(pubkey_b58.decode("utf-8")))
## Read amount
if price_type == 0: # Free price, ignore amount
qr.read(amount_len)
print("Free price")
elif price_type == 1: # Units
amount = varint.decode_stream(qr)
# print("Price: {} Ğ1".format(amount/100))
elif price_type == 2: # UD
amount_n = varint.decode_stream(qr)
amount_e = varint.decode_stream(qr)
amount = amount_n * 10 ** -amount_e
# print("Price: {} UD_Ğ1".format(amount.decode("utf-8")))
else:
qr.read(amount_len)
print("Error: unknown price type, ignoring price")
## Read data
if data_type == 0: # No data
data = None
print("There is no data")
elif data_type == 1: # Plain text
data = qr.read()
print("Data:")
print(data)
elif data_type == 2: # Ğchange ad
data = base64.urlsafe_b64encode(qr.read(16))
# print("Ğchange ad ID: {}".format(data.decode("utf-8")))
## Get gchange-pod datas
item = os.popen("./../jaklis/jaklis.py getoffer -i {0}".format(data.decode("utf-8")))
# item = cs.getOffer(id)
jsonR = json.load(item)
item_time = jsonR['creationTime']
item_name = jsonR['title']
item_description = jsonR['description']
item_image = jsonR['thumbnail']
isImage = '_content' in item_image
if (isImage):
print(item_image['_content'])
# print(jsonR)
print(item_time)
print(item_name)
print(item_description)

View File

@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
hasError=0
for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel; do 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 if [ $(dpkg-query -W -f='${Status}' $i 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
[[ ! $j ]] && sudo apt update [[ ! $j ]] && sudo apt update
@ -10,12 +8,5 @@ for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel;
fi fi
done done
pip3 install -r requirements.txt || hasError=1 pip3 install -r requirements.txt
chmod u+x jaklis.py chmod u+x jaklis.py
sudo ln -sf $(realpath jaklis.py) /usr/local/bin/jaklis || hasError=1
if [[ hasError -eq 0 ]]; then
echo "Setup done. You can use 'jaklis' command, try it."
else
echo "An error has occurred"
fi