Compare commits

...

88 Commits

Author SHA1 Message Date
poka 4dc34fb64e update requirements 2023-09-15 10:16:33 +02:00
poka cb7037072e add dict annotation 2023-09-13 09:06:44 +02:00
poka 1737080b82 fix command with no args 2023-09-10 11:33:31 +02:00
poka 56f5972ad0 refacto args passing 2023-09-10 09:44:52 +02:00
poka 5fe53c1d4c refacto arguments parsing 2023-09-10 08:15:56 +02:00
poka 919a4171f5 bump 0.1.0 2023-09-10 02:23:44 +02:00
poka 6dc86592b9 refacto and translate main file 2023-09-10 02:03:42 +02:00
poka 595f7722bf refacto #1 2023-09-10 01:53:38 +02:00
poka e188d4da94 unindent json 2023-09-09 22:35:35 +02:00
poka aa6fd2b738 antispam: improve condition 2023-09-09 13:26:27 +02:00
poka 676fbbfa9b antispam: remove unknown wallet by GVA 2023-09-09 12:51:47 +02:00
poka 3492643d4f add map option to walletList 2023-09-09 10:14:31 +02:00
poka b1049bd7b7 Merge branch 'refactoGeolocProfiles' 2023-09-09 09:50:52 +02:00
poka 2c5435a18b add sublevel in json, with timestamp 2023-09-09 09:50:20 +02:00
poka 51ae54484c remove unused imports 2023-09-09 09:44:27 +02:00
poka e0ba392671 add geolocProfiles command 2023-09-09 09:38:22 +02:00
poka ab50340595 update vscode settings 2023-09-09 04:26:50 +02:00
poka b89456a9b4 Merge branch 'g1walletbot' 2023-09-09 00:16:01 +02:00
Kapis a13723fa3c showing hash in json output 2023-03-16 18:45:34 +01:00
poka bdc8b938fc Merge pull request 'Adding blockstamp to history JSON' (#24) from g1walletbot into master
Reviewed-on: axiom-team/jaklis#24
2023-03-14 21:20:18 +01:00
Kapis fc587819b0 Adding blockstamp to history JSON 2023-03-14 20:35:17 +01:00
poka b6dbc5f8da Merge pull request 'JSON history added field status and new GVA node' (#23) from kapis/jaklis:master into master
Reviewed-on: axiom-team/jaklis#23
2023-03-11 21:18:20 +01:00
Kapis dbbd2c3ed6 more info about dunikey path 2023-03-11 21:12:57 +01:00
Kapis 31f4fc1f83 added Poka node 2023-03-10 22:21:33 +01:00
Kapis 4a1cae165a Added pini GVA node 2023-03-10 22:21:33 +01:00
Kapis 249aa2c6a7 adding status to json history output 2023-03-10 22:21:33 +01:00
Kapis afd244d405 ignoring pycache 2023-03-10 22:21:33 +01:00
poka b047142b64 change default duniter node 2023-03-10 22:20:58 +01:00
poka 0c264fc57c hello aya 2023-01-21 11:31:19 +01:00
poka afad8092b7 fix setup symlink 2023-01-21 11:29:29 +01:00
poka f99b6558a2 fix fucking down CS POD in .env.template 2022-11-23 01:41:04 +01:00
poka a882593ead update .env.template 2022-10-23 04:27:00 +02:00
poka 8690615335 fix GVA comment transaction sentence 2022-10-23 04:21:30 +02:00
poka 60bae39fba fix everything in universe and more 2022-10-23 04:05:48 +02:00
poka 0d7db5ba17 remove tata mother fucker 2022-10-23 03:44:37 +02:00
poka 8daee665e4 fix bad nonce on some message 2022-10-23 03:42:18 +02:00
poka 5a7b0c6901 fix null message content 2022-10-23 03:31:27 +02:00
poka ec7fcdfb45 Replace NODE and POD envvar with DUNITER and ESNODE, like g1stats 2022-03-29 00:27:00 +02:00
poka 9117def4b9 Change cesium pods and duniter node ofr good one; Fix MY_PATH for symlink; Add jaklis to env in setup.sh 2021-12-08 11:12:51 +01:00
poka efed7354df listWallets: Show balance in G1, not cents 2021-10-18 01:31:28 +02:00
poka db39e5c77d Mise à jour de 'README.md' 2021-10-09 12:51:26 +02:00
poka b291ddaef5 test git 2021-10-07 10:42:01 +02:00
poka 9270fb7ba2 Add username for non mbr 2021-08-28 06:56:29 +02:00
poka 53cbdfe982 Add username in mbr brut list 2021-08-28 03:11:11 +02:00
poka e8aa6723ee Improve listWallets options and algo 2021-08-28 02:47:20 +02:00
poka 314dc74404 fix cursor 2021-08-27 23:08:38 +02:00
poka 7bfa733fc5 fix typo 2021-08-27 23:02:08 +02:00
poka 43e5836eef fix listWallets with no dunikey 2021-08-27 22:59:16 +02:00
poka 116b362ea9 Add listWallet command 2021-08-27 05:02:16 +02:00
poka a6f26331d7 Merge pull request 'default offer category = CD/DVD' (#17) from qo-op/jaklis:master into master
Reviewed-on: axiom-team/jaklis#17
2021-07-02 01:14:20 +02:00
qo-op 30e206d61d default offer category = CD/DVD 2021-07-02 00:08:35 +02:00
Jean-Jacques Brucker b7fa622941 finish previous shebang fix + minor improvement in code and help message 2021-07-01 11:24:40 +02:00
poka fecc636418 Merge pull request 'To work with python 3.6 on Linux Mint' (#16) from qo-op/jaklis:master into master
Reviewed-on: axiom-team/jaklis#16
2021-07-01 02:24:40 +02:00
qo-op 864334d06d To work with python 3.6 on Linux Mint
Remove 3.7 definition...
2021-06-30 21:11:04 +02:00
poka 75e71763f8 FIx comments labels 2021-06-30 06:14:16 +02:00
poka 6cb7e36bd6 Add command currentUd 2021-06-30 06:10:48 +02:00
poka 4e6c100ef7 Go to duniterpy 0.62.0 2021-06-30 05:51:36 +02:00
poka d9de7c8838 Change likes file to stars 2021-06-30 05:29:22 +02:00
poka 087d5debe6 Fix null balance 2021-06-30 04:34:15 +02:00
poka b4de0b3f0a Come back to gql 3.0+5 cause of AIOHTTPTransport : https://stackoverflow.com/a/67428133 2021-06-30 03:52:41 +02:00
poka b302b50ef4 Set gql 2.0 2021-06-30 03:44:23 +02:00
poka e1957d38ef Set same checksum format for pubkey as Gecko 2021-06-30 03:29:04 +02:00
poka 23198600d3 Fix pay GVA query 2021-06-30 02:42:32 +02:00
poka 7e255b20f0 Fix history and balance with new GVA version 2021-06-30 02:07:19 +02:00
poka 9b33fc77a4 Add comment for Duniter node 2021-06-24 13:19:58 +02:00
poka 4a0bcab7c7 Amand README 2021-06-24 13:04:31 +02:00
poka 950125d3f9 Fix get ID 2021-03-31 11:32:05 +02:00
poka 1a636302fd Add geoloc in get 2021-03-30 15:12:10 +02:00
poka 30f4585223 Fix scalar types for balance and idty and history GVA 2021-03-29 12:32:38 +02:00
poka fb4bed8dc2 Add idBalance command 2021-02-26 04:11:02 +01:00
poka 0663d11287 update requirements 2021-02-26 02:46:40 +01:00
poka 963eaef71a Fix no need keystore 2021-02-26 02:32:10 +01:00
poka e76bc58e9f Add id command, to get username and isMember pubkey 2021-02-26 02:25:07 +01:00
poka 072604ebe5 Mise à jour de 'README.md' 2021-02-20 18:35:41 +01:00
poka 4619baa41f Fix add picture to offer 2021-02-14 18:36:50 +01:00
poka 1f87d56cbd Merge branch 'manageOffers' 2021-02-14 18:19:10 +01:00
poka 0e38e16cbd Can get, set, delete an offer. enjoy 2021-02-14 18:18:01 +01:00
poka 1015a75a26 Change files mode 2021-02-14 15:08:31 +01:00
poka b64eeefd17 update README: python 3.8 minimum 2020-12-27 22:01:11 +01:00
poka 9a64979aa7 Full history with pagination 2020-12-15 21:19:36 +01:00
poka 66c2070ef8 Use new History shema with paginiation 2020-12-15 20:15:18 +01:00
poka 24a58e7e03 Remplace like command name by stars 2020-12-15 19:53:25 +01:00
poka 68a57bd5c1 Add requests to requirements.txt 2020-12-13 14:58:45 +01:00
poka 0841ffcf49 Skip dunikey if don't needed 2020-12-08 23:15:24 +01:00
poka fd0f4dbe16 Conditionnal import into code ... so bad 2020-12-08 22:02:20 +01:00
poka b78241584d Add gql to setup 2020-12-08 21:44:17 +01:00
poka 9e933cbaf4 Edit readme 2020-12-08 21:25:30 +01:00
poka 2c7fd15749 Add node to .env.template 2020-12-08 20:14:34 +01:00
24 changed files with 1541 additions and 316 deletions

20
.env.template Normal file → Executable file
View File

@ -1,4 +1,16 @@
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
# Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec
# ex. DUNIKEY="/path/myprivateG1key.dunikey"
DUNIKEY=""
# Noeud Duniter
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

3
.gitignore vendored Normal file → Executable file
View File

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

7
.vscode/settings.json vendored Executable file
View File

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

34
README.md Normal file → Executable file
View File

@ -13,6 +13,8 @@ Débrouillez-vous.
## Utilisation
*Python 3.6 minimum*
Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template).
```
@ -20,24 +22,34 @@ 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,stars,unstars,getoffer,setoffer,deleteoffer,pay,history,balance,id,idBalance} ...
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,stars,unstars,getoffer,setoffer,deleteoffer,pay,history,balance,id,idBalance}
read Lecture des messages
send Envoi d'un message
delete Supression d'un message
get Voir un profile Cesium+
set Configurer son profile Cesium+
erase Effacer son profile Cesium+
like Voir les likes d'un profile / Liker un profile (option -s NOTE)
unlike Supprimer un like
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
stars Voir les étoiles d'un profile / Noter un profile (option -s NOTE)
unstars Supprimer un star
getoffer Obtenir les informations d'une annonce gchange
setoffer Créer une annonce gchange
deleteoffer Supprimer une annonce gchange
pay Payer en Ḡ1
history Voir l'historique des transactions 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.

2
dunikey.pubsec Executable file
View File

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

543
jaklis.py
View File

@ -1,150 +1,395 @@
#!/usr/bin/env python3
import argparse, sys, os, getpass, string, random
from os.path import join, dirname
from shutil import copyfile
import argparse
import sys
import os
import string
import random
from dotenv import load_dotenv
from duniterpy.key import SigningKey
from lib.cesium import CesiumPlus
from pathlib import Path
from lib.gva import GvaApi
from lib.cesium import CesiumPlus
__version__ = "0.0.2"
__version__ = "0.1.1"
MY_PATH = os.path.realpath(os.path.dirname(sys.argv[0])) + '/'
MY_PATH = Path(__file__).resolve().parent
# 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)
# Set file paths
dotenv_file = MY_PATH / ".env"
dotenv_template = MY_PATH / ".env.template"
# Parse arguments
parser = argparse.ArgumentParser(description="Client CLI pour Cesium+ et Ḡchange")
parser.add_argument('-v', '--version', action='store_true', help="Affiche la version actuelle du programme")
parser.add_argument('-k', '--key', help="Chemin vers mon trousseau de clé (PubSec)")
parser.add_argument('-n', '--node', help="Adresse du noeud Cesium+, Gchange ou Duniter à utiliser")
# Check and create dotenv file
if not dotenv_file.is_file():
dotenv_file.write_text(dotenv_template.read_text())
subparsers = parser.add_subparsers(title="Commandes de jaklis", dest="cmd")
read_cmd = subparsers.add_parser('read', help="Lecture des messages")
send_cmd = subparsers.add_parser('send', help="Envoi d'un message")
delete_cmd = subparsers.add_parser('delete', help="Supression d'un message")
getProfile_cmd = subparsers.add_parser('get', help="Voir un profile Cesium+")
setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesium+")
eraseProfile_cmd = subparsers.add_parser('erase', help="Effacer son profile Cesium+")
like_cmd = subparsers.add_parser('like', help="Voir les likes d'un profile / Liker un profile (option -s NOTE)")
unlike_cmd = subparsers.add_parser('unlike', help="Supprimer un like")
pay_cmd = subparsers.add_parser('pay', help="Payer en Ḡ1")
history_cmd = subparsers.add_parser('history', help="Voir l'historique des transactions d'un compte Ḡ1")
balance_cmd = subparsers.add_parser('balance', help="Voir le solde d'un compte Ḡ1")
# Load environment variables
load_dotenv(dotenv_file)
# Messages management
read_cmd.add_argument('-n', '--number',type=int, default=3, help="Affiche les NUMBER derniers messages")
read_cmd.add_argument('-j', '--json', action='store_true', help="Sort au format JSON")
read_cmd.add_argument('-o', '--outbox', action='store_true', help="Lit les messages envoyés")
# Set global values (default parameters) regarding environment variables
node = os.getenv("DUNITER") + "/gva" or "https://g1v1.p2p.legal/gva"
pod = os.getenv("ESNODE") or "https://g1.data.e-is.pro"
destPubkey = False
send_cmd.add_argument('-d', '--destinataire', required=True, help="Destinataire du message")
send_cmd.add_argument('-t', '--titre', help="Titre du message à envoyer")
send_cmd.add_argument('-m', '--message', help="Message à envoyer")
send_cmd.add_argument('-f', '--fichier', help="Envoyer le message contenu dans le fichier 'FICHIER'")
send_cmd.add_argument('-o', '--outbox', action='store_true', help="Envoi le message sur la boite d'envoi")
# define parser
parser = argparse.ArgumentParser(
description="CLI Client for Cesium+ and Ḡchange",
epilog="current node: '" + node + "', current pod: '" + pod + "'.",
)
delete_cmd.add_argument('-i', '--id', action='append', nargs='+', required=True, help="ID(s) du/des message(s) à supprimer")
delete_cmd.add_argument('-o', '--outbox', action='store_true', help="Suppression d'un message envoyé")
# load global arguments
parser.add_argument(
"-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"
)
# Profiles management
setProfile_cmd.add_argument('-n', '--name', help="Nom du profile")
setProfile_cmd.add_argument('-d', '--description', help="Description du profile")
setProfile_cmd.add_argument('-v', '--ville', help="Ville du profile")
setProfile_cmd.add_argument('-a', '--adresse', help="Adresse du profile")
setProfile_cmd.add_argument('-pos', '--position', nargs=2, help="Points géographiques (lat + lon)")
setProfile_cmd.add_argument('-s', '--site', help="Site web du profile")
setProfile_cmd.add_argument('-A', '--avatar', help="Chemin vers mon avatar en PNG")
getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile")
getProfile_cmd.add_argument('-a', '--avatar', action='store_true', help="Récupérer également l'avatar au format raw base64")
# Likes management
like_cmd.add_argument('-p', '--profile', help="Profile cible")
like_cmd.add_argument('-s', '--stars', type=int, help="Nombre d'étoile")
unlike_cmd.add_argument('-p', '--profile', help="Profile à déliker")
# GVA usage
pay_cmd.add_argument('-p', '--pubkey', help="Destinataire du paiement")
pay_cmd.add_argument('-a', '--amount', type=float, help="Montant de la transaction")
pay_cmd.add_argument('-c', '--comment', default="", help="Commentaire de la transaction")
pay_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool")
pay_cmd.add_argument('-v', '--verbose', action='store_true', help="Affiche le résultat JSON de la transaction")
history_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé")
history_cmd.add_argument('-j', '--json', action='store_true', help="Affiche le résultat en format JSON")
history_cmd.add_argument('--nocolors', action='store_true', help="Affiche le résultat en noir et blanc")
balance_cmd.add_argument('-p', '--pubkey', help="Clé publique du compte visé")
balance_cmd.add_argument('-m', '--mempool', action='store_true', help="Utilise les sources en Mempool")
# Define commands with arguments
commands = {
"read": {
"help": "Read messages",
"arguments": {
("n", "number"): {
"type": int,
"default": 3,
"help": "Display the last NUMBER messages",
},
("j", "json"): {"action": "store_true", "help": "Output in JSON format"},
("o", "outbox"): {"action": "store_true", "help": "Read sent messages"},
},
"type": "cesium",
},
"send": {
"help": "Send a message",
"arguments": {
("d", "destinataire"): {
"required": True,
"help": "Recipient of the message",
},
("t", "titre"): {"help": "Title of the message to send"},
("m", "message"): {"help": "Message to send"},
("f", "fichier"): {"help": "Send the message from the 'FILE'"},
("o", "outbox"): {
"action": "store_true",
"help": "Send the message to the outbox",
},
},
"type": "cesium",
},
"delete": {
"help": "Delete a message",
"arguments": {
("i", "id"): {
"action": "append",
"nargs": "+",
"required": True,
"help": "ID(s) of the message(s) to delete",
},
("o", "outbox"): {
"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_dict = vars(args)
cmd = args.cmd
if args.version:
print(__version__)
sys.exit(0)
print(__version__)
sys.exit(0)
if not cmd:
parser.print_help()
sys.exit(1)
sys.exit(0)
def createTmpDunikey():
# Generate pseudo-random nonce
nonce=[]
for _ in range(32):
nonce.append(random.choice(string.ascii_letters + string.digits))
nonce = ''.join(nonce)
# Generate a pseudo-random nonce
nonce = "".join(
random.choice(string.ascii_letters + string.digits) for _ in range(32)
)
keyPath = "/tmp/secret.dunikey-" + nonce
key = SigningKey.from_credentials(getpass.getpass("Identifiant: "), getpass.getpass("Mot de passe: "), None)
# Create a dummy key (replace with actual key creation logic)
key = SigningKey.from_credentials(
"sgse547yhd54xv6541srdh", "sfdgwdrhpkxdawsbszqpof1sdg65xc", None
)
key.save_pubsec_file(keyPath)
return keyPath
if args.key:
dunikey = args.key
keyPath = False
else:
dunikey = os.getenv('DUNIKEY')
def get_arg_value(args, arg):
try:
return getattr(args, arg)
except AttributeError:
return False
def get_dunikey(args):
if args.key:
return args.key
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)
HOME = os.getenv("HOME")
dunikey = HOME + dunikey
if not os.path.isfile(dunikey):
sys.stderr.write("The keyfile {0} is not found.\n".format(dunikey))
sys.exit(1)
return dunikey
# Construct CesiumPlus object
if cmd in ("read","send","delete","set","get","erase","like","unlike"):
if args.node:
pod = args.node
else:
pod = os.getenv('POD')
if not pod:
pod="https://g1.data.le-sou.org"
pubkey = get_arg_value(args, "pubkey")
profile = get_arg_value(args, "profile")
cesium = CesiumPlus(dunikey, pod)
noNeedDunikey = cmd in (
"history",
"balance",
"page",
"id",
"idBalance",
"listWallets",
"geolocProfiles",
) and (pubkey or profile)
if 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
if cmd == "read":
cesium.read(args.number, args.outbox, args.json)
cesium.read(*cmd_args_values)
elif cmd == "send":
if args.fichier:
with open(args.fichier, 'r') as f:
with open(args.fichier, "r") as f:
msgT = f.read()
titre = msgT.splitlines(True)[0].replace('\n', '')
msg = ''.join(msgT.splitlines(True)[1:])
titre = msgT.splitlines(True)[0].replace("\n", "")
msg = "".join(msgT.splitlines(True)[1:])
if args.titre:
titre = args.titre
msg = msgT
@ -152,8 +397,8 @@ if cmd in ("read","send","delete","set","get","erase","like","unlike"):
titre = args.titre
msg = args.message
else:
titre = input("Indiquez le titre du message: ")
msg = input("Indiquez le contenu du message: ")
titre = input("Enter the message title: ")
msg = input("Enter the message content: ")
cesium.send(titre, msg, args.destinataire, args.outbox)
@ -162,44 +407,84 @@ if cmd in ("read","send","delete","set","get","erase","like","unlike"):
# Profiles
elif cmd == "set":
cesium.set(args.name, args.description, args.ville, args.adresse, args.position, args.site, args.avatar)
cesium.set(**cmd_args_dict)
elif cmd == "get":
cesium.get(args.profile, args.avatar)
cesium.get(**cmd_args_dict)
elif cmd == "page":
cesium.getPage(**cmd_args_dict)
elif cmd == "erase":
cesium.erase()
elif cmd == "geolocProfiles":
cesium.geolocProfiles(node)
# Likes
elif cmd == "like":
if args.stars or args.stars == 0:
cesium.like(args.stars, args.profile)
# Stars
elif cmd == "stars":
if args.number or args.number == 0:
cesium.like(args.number, args.profile)
else:
cesium.readLikes(args.profile)
elif cmd == "unlike":
elif cmd == "unstars":
cesium.unLike(args.profile)
# Construct GVA object
elif cmd in ("pay","history","balance"):
if args.node:
node = args.node
# Offers
elif cmd == "getoffer":
cesium.getOffer(args.id)
elif cmd == "setoffer":
cesium.setOffer(**cmd_args_dict)
elif cmd == "deleteoffer":
cesium.deleteOffer(**cmd_args_dict)
else:
node = os.getenv('NODE')
if not node:
node="https://g1.librelois.fr/gva"
raise ValueError(f"Unknown command: {cmd}")
if args.pubkey:
destPubkey = args.pubkey
else:
destPubkey = False
gva = GvaApi(dunikey, node, destPubkey)
def handle_gva_commands(args, cmd, gva):
# 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":
gva.pay(args.amount, args.comment, args.mempool, args.verbose)
if cmd == "history":
gva.history(args.json, args.nocolors)
if cmd == "balance":
elif cmd == "history":
gva.history(args.json, args.nocolors, args.number)
elif cmd == "balance":
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:
os.remove(keyPath)

129
lib/cesium.py Normal file → Executable file
View File

@ -1,15 +1,19 @@
import json
import re, string, random, base64
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.profiles import Profiles
from lib.likes import ReadLikes, SendLikes, UnLikes
from lib.stars import ReadLikes, SendLikes, UnLikes
from lib.offers import Offers
class CesiumPlus(CesiumCommon):
#################### Messaging ####################
def read(self, nbrMsg, outbox, isJSON):
readCesium = ReadFromCesium(self.dunikey, self.pod)
def read(self, nbrMsg, isJSON, outbox):
readCesium = ReadFromCesium(self.dunikey, self.pod)
jsonMsg = readCesium.sendDocument(nbrMsg, outbox)
if isJSON:
jsonFormat = readCesium.jsonMessages(jsonMsg, nbrMsg, outbox)
@ -22,16 +26,18 @@ class CesiumPlus(CesiumCommon):
sendCesium.recipient = recipient
# Generate pseudo-random nonce
nonce=[]
nonce = []
for _ in range(32):
nonce.append(random.choice(string.ascii_letters + string.digits))
sendCesium.nonce = base64.b64decode(''.join(nonce))
sendCesium.nonce = base64.b64decode("".join(nonce))
finalDoc = sendCesium.configDoc(sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg)) # Configure JSON document to send
sendCesium.sendDocument(finalDoc, outbox) # Send final signed document
finalDoc = sendCesium.configDoc(
sendCesium.encryptMsg(title), sendCesium.encryptMsg(msg)
) # Configure JSON document to send
sendCesium.sendDocument(finalDoc, outbox) # Send final signed document
def delete(self, idsMsgList, outbox):
deleteCesium = DeleteFromCesium(self.dunikey, self.pod)
deleteCesium = DeleteFromCesium(self.dunikey, self.pod)
# deleteCesium.issuer = recipient
for idMsg in idsMsgList:
finalDoc = deleteCesium.configDoc(idMsg, outbox)
@ -39,40 +45,74 @@ class CesiumPlus(CesiumCommon):
#################### Profiles ####################
def set(self, name=None, description=None, ville=None, adresse=None, position=None, site=None, avatar=None):
setProfile = Profiles(self.dunikey, self.pod)
document = setProfile.configDocSet(name, description, ville, adresse, position, site, avatar)
result = setProfile.sendDocument(document,'set')
def set(
self,
name=None,
description=None,
ville=None,
adresse=None,
position=None,
site=None,
avatar=None,
):
setProfile = Profiles(self.dunikey, self.pod)
document = setProfile.configDocSet(
name, description, ville, adresse, position, site, avatar
)
result = setProfile.sendDocument(document, "set")
print(result)
return result
def get(self, profile=None, avatar=None):
getProfile = Profiles(self.dunikey, self.pod)
getProfile = Profiles(self.dunikey, self.pod, self.noNeedDunikey)
if not profile:
profile = self.pubkey
if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45:
scope = 'title'
scope = "title"
else:
scope = '_id'
scope = "_id"
document = getProfile.configDocGet(profile, scope, avatar)
resultJSON = getProfile.sendDocument(document, 'get')
resultJSON = getProfile.sendDocument(document, "get")
result = getProfile.parseJSON(resultJSON)
print(result)
def erase(self):
eraseProfile = Profiles(self.dunikey, self.pod)
document = eraseProfile.configDocErase()
result = eraseProfile.sendDocument(document,'erase')
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):
eraseProfile = Profiles(self.dunikey, self.pod)
document = eraseProfile.configDocErase()
result = eraseProfile.sendDocument(document, "erase")
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 ####################
def readLikes(self, profile=False):
likes = ReadLikes(self.dunikey, self.pod)
likes = ReadLikes(self.dunikey, self.pod, self.noNeedDunikey)
document = likes.configDoc(profile)
result = likes.sendDocument(document)
result = likes.parseResult(result)
@ -80,14 +120,51 @@ class CesiumPlus(CesiumCommon):
print(result)
def like(self, stars, profile=False):
likes = SendLikes(self.dunikey, self.pod)
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)
likes = UnLikes(self.dunikey, self.pod)
idLike = likes.checkLike(pubkey)
if idLike:
document = likes.configDoc(idLike)
likes.sendDocument(document, silent)
#################### Offer ####################
def setOffer(
self,
title=None,
description=None,
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)
return result
def getOffer(self, id, avatar=None):
getOffer = Offers(self.dunikey, self.pod, self.noNeedDunikey)
resultJSON = getOffer.sendDocumentGet(id, "get")
# print(resultJSON)
result = getOffer.parseJSON(resultJSON)
print(result)
def deleteOffer(self, id):
eraseOffer = Offers(self.dunikey, self.pod)
document = eraseOffer.configDocErase(id)
result = eraseOffer.sendDocumentSet(document, "delete", id)
print(result)

10
lib/cesiumCommon.py Normal file → Executable file
View File

@ -13,7 +13,9 @@ def pp_json(json_thing, sort=True, indents=4):
return None
class CesiumCommon:
def __init__(self, dunikey, pod):
def __init__(self, dunikey, pod, noNeedDunikey=False):
self.pod = pod
self.noNeedDunikey = noNeedDunikey
# Get my pubkey from my private key
try:
self.dunikey = dunikey
@ -23,8 +25,10 @@ class CesiumCommon:
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 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")

40
lib/currentUd.py Executable file
View File

@ -0,0 +1,40 @@
#!/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

112
lib/geolocProfiles.py Executable file
View File

@ -0,0 +1,112 @@
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())}

125
lib/getPages.py Executable file
View File

@ -0,0 +1,125 @@
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'

38
lib/gva.py Normal file → Executable file
View File

@ -1,14 +1,22 @@
from lib.currentUd import currentUd
from lib.gvaWallets import ListWallets
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
from lib.gvaID import Id
class GvaApi():
def __init__(self, dunikey, node, pubkey):
def __init__(self, dunikey, node, pubkey, noNeedDunikey=False):
self.noNeedDunikey = noNeedDunikey
self.dunikey = dunikey
self.node = node
self.pubkey = get_privkey(dunikey, "pubsec").pubkey
if noNeedDunikey:
self.pubkey = self.dunikey
else:
self.pubkey = get_privkey(dunikey, "pubsec").pubkey
if pubkey:
self.destPubkey = pubkey
else:
@ -31,15 +39,16 @@ class GvaApi():
#################### Payments ####################
def pay(self, amount, comment, mempool, verbose):
comment = " ".join(comment)
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):
def history(self, isJSON=False, noColors=False, number=10):
gva = History(self.dunikey, self.node, self.destPubkey)
gva.sendDoc()
gva.sendDoc(number)
transList = gva.parseHistory()
if isJSON:
@ -52,3 +61,24 @@ class GvaApi():
gva = Balance(self.dunikey, self.node, self.destPubkey, useMempool)
balanceValue = gva.sendDoc()
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)

7
lib/gvaBalance.py Normal file → Executable file
View File

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

201
lib/gvaHistory.py Normal file → Executable file
View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3
import sys, re, os.path, json, ast, time
import sys, re, os.path, json, ast, time, hashlib
from datetime import datetime
from duniterpy.key import base58
from termcolor import colored
from lib.natools import fmt, sign, get_privkey
from gql import gql, Client
@ -23,36 +24,55 @@ class History:
transport = AIOHTTPTransport(url=node)
self.client = Client(transport=transport, fetch_schema_from_transport=True)
def sendDoc(self):
def sendDoc(self, number):
# 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
query ($pubkey: PubKeyGva!, $script: PkOrScriptGva!, $number: Int!){
txsHistoryBc(
script: $script
pagination: { pageSize: $number, ord: DESC }
) {
both {
pageInfo {
hasPreviousPage
hasNextPage
}
edges {
direction
node {
currency
issuers
blockstamp
outputs
comment
writtenTime
hash
}
}
}
}
balance(script: $pubkey) {
txsHistoryMp(pubkey: $pubkey) {
receiving {
currency
issuers
comment
outputs
receivedTime
blockstamp
hash
}
sending {
currency
issuers
comment
outputs
receivedTime
blockstamp
hash
}
}
balance(script: $script) {
amount
base
}
@ -69,7 +89,9 @@ class History:
"""
)
paramsBuild = {
"pubkey": self.pubkey
"pubkey": self.pubkey,
"number": number,
"script": f"SIG({self.pubkey})",
}
# Send history document
@ -88,34 +110,71 @@ class History:
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]
# Parse transactions in blockchain
resBc = []
resBc = self.historyDoc['txsHistoryBc']['both']['edges']
for j, transaction in enumerate(resBc):
# print(transaction)
direction = resBc[j]['direction']
transaction = resBc[j]['node']
output = transaction['outputs'][0]
outPubkey = output.split("SIG(")[1].replace(')','')
# if direction == 'RECEIVED' or self.pubkey != outPubkey:
trans.append(i)
trans[i] = []
trans[i].append(direction)
trans[i].append(transaction['writtenTime'])
if direction == 'SENT':
trans[i].append(outPubkey)
amount = int('-' + output.split(':')[0])
else:
trans[i].append(transaction['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(transaction['comment'])
trans[i].append(base)
trans[i].append(transaction['blockstamp'])
trans[i].append(transaction['hash'])
i += 1
# Parse transactions in mempool
for direction, resBc in self.historyDoc['txsHistoryMp'].items():
for j, transaction in enumerate(resBc):
# print(transaction)
transaction = resBc[j]
output = transaction['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
# if direction == 'RECEIVING' or self.pubkey != outPubkey:
trans.append(i)
trans[i] = []
trans[i].append(direction.upper())
trans[i].append(transaction['receivedTime'])
# trans[i].append(int(time.time()))
if direction.upper() == 'SENDING':
trans[i].append(outPubkey)
amount = int('-' + output.split(':')[0])
else:
trans[i].append(transaction['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(transaction['comment'])
trans[i].append(base)
trans[i].append(transaction['blockstamp'])
trans[i].append(transaction['hash'])
i += 1
# Order transactions by date
trans.sort(key=lambda x: x[1])
@ -130,8 +189,12 @@ class History:
def printHistory(self, trans, noColors):
# Get balance
balance = self.historyDoc['balance']['amount']/100
balanceUD = round(balance/self.UD, 2)
if (self.historyDoc['balance'] == None):
balance = balanceUD = 'null'
else:
balance = self.historyDoc['balance']['amount']/100
balanceUD = round(balance/self.UD, 2)
# Get currency
currency = self.historyDoc['node']['peer']['currency']
@ -152,13 +215,14 @@ class History:
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"
if t[0] == "RECEIVED": color = "green"
elif t[0] == "SENT": color = "blue"
elif t[0] == "receiving": color = "yellow"
elif t[0] == "sending": color = "red"
else: color = "blue"
else: color = None
if noColors:
color = None
if t[0] in ('receiving','sending'):
if t[0] in ('RECEIVING','SENDING'):
comment = '(EN ATTENTE) ' + t[5]
else:
comment = t[5]
@ -173,11 +237,12 @@ class History:
print('|', end='')
print('-'.center(rows-1, '-'))
print('|', end='')
printKey = t[2][0:8] + '\u2026' + t[2][-3:]
checksum = self.gen_checksum(t[2])
shortPubkey = t[2][0:4] + '\u2026' + t[2][-4:] + ':' + checksum
if noColors:
print(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment))
print(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, shortPubkey, t[3], t[4], comment))
else:
print(colored(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, printKey, t[3], t[4], comment), color))
print(colored(" {: <18} | {: <12} | {: <7} | {: <7} | {: <30}".format(date, shortPubkey, t[3], t[4], comment), color))
print('|', end='')
print('-'.center(rows-1, '-'))
print('|', end='')
@ -188,17 +253,29 @@ class History:
print(colored('Reçus', 'green'), '-', colored('En cours de réception', 'yellow'), '-', colored('Envoyé', 'blue'), '-', colored("En cours d'envoi", 'red'))
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):
dailyJSON = []
for i, trans in enumerate(transList):
dailyJSON.append(i)
dailyJSON[i] = {}
dailyJSON[i]['status'] = trans[0].upper()
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[i]['blockstamp'] = trans[7]
dailyJSON[i]['hash'] = trans[8]
dailyJSON = json.dumps(dailyJSON, indent=2)
# If we want to write JSON to a file

81
lib/gvaID.py Executable file
View File

@ -0,0 +1,81 @@
#!/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)

4
lib/gvaPay.py Normal file → Executable file
View File

@ -6,7 +6,7 @@ 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}"
PUBKEY_REGEX = "(?![OIl])[0-9A-Za-z]{42,45}"
class Transaction:
@ -46,7 +46,7 @@ class Transaction:
if self.verbose: print("useMempool:", str(self.useMempool))
queryBuild = gql(
"""
query ($recipient: String!, $issuer: String!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx(
query ($recipient: PkOrScriptGva!, $issuer: PubKeyGva!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx(
amount: $amount
comment: $comment
issuer: $issuer

100
lib/gvaWallets.py Executable file
View File

@ -0,0 +1,100 @@
#!/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)

26
lib/messaging.py Normal file → Executable file
View File

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

138
lib/offers.py Executable file
View File

@ -0,0 +1,138 @@
import sys, re, json, requests, base64
from time import time
from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
class Offers(CesiumCommon):
# Configure JSON document SET to send
def configDocSet(self, title, description, city, location, category, price: float, picture):
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"}
data = {}
if title: data['title'] = title
if description: data['description'] = description
if city: data['city'] = city
if location:
geoPoint = {}
geoPoint['lat'] = location[0]
geoPoint['lon'] = location[1]
data['geoPoint'] = geoPoint
if picture:
picture = open(picture, 'rb').read()
picture = base64.b64encode(picture).decode()
data['thumbnail'] = {}
data['thumbnail']['_content'] = picture
data['thumbnail']['_content_type'] = "image/png"
# if category: data['category'] = category
# else:
data['category'] = {"parent":"cat24","localizedNames":{"en":"DVD / Films","es-ES":"DVDs / Cine","fr-FR":"DVD / Films"},"name":"DVD / Films","id":"cat25"}
if price: data['price'] = float(price) * 100
data['type'] = 'offer'
data['time'] = timeSent
data['creationTime'] = timeSent
data['issuer'] = self.pubkey
data['pubkey'] = self.pubkey
data['version'] = 2
data['currency'] = 'g1'
data['unit'] = None
data['fees'] = None
data['feesCurrency'] = None
if picture: data['picturesCount'] = 1
else: data['picturesCount'] = 0
data['stock'] = 1
data['tags'] = []
document = json.dumps(data)
return self.signDoc(document)
# Configure JSON document SET to send
def configDocErase(self, id):
timeSent = int(time())
# "currency":"g1","unit":null,"fees":null,"feesCurrency":null,"picturesCount":0,"stock":0,"tags":[],"id":"AXehXeyZaml2THvBAeS5","creationTime":1613320117}
#AXehXeyZaml2THvBAeS5
offerToDeleteBrut = self.sendDocumentGet(id, 'get')
offerToDelete = json.loads(self.parseJSON(offerToDeleteBrut))
title = offerToDelete['title']
creationTime = offerToDelete['time']
issuer = offerToDelete['issuer']
pubkey = offerToDelete['pubkey']
data = {}
data['title'] = title
data['time'] = timeSent
data['creationTime'] = creationTime
data['id'] = id
data['issuer'] = issuer
data['pubkey'] = pubkey
data['version'] = 2
data['type'] = "offer"
data['currency'] = "g1"
data['unit'] = None
data['fees'] = None
data['feesCurrency'] = None
data['picturesCount'] = 0
data['stock'] = 0
data['tags'] = []
document = json.dumps(data)
return self.signDoc(document)
def sendDocumentGet(self, id, type):
headers = {
'Content-type': 'application/json',
}
# Send JSON document and get JSON result
if type == 'set':
reqQuery = '{0}/market/record'.format(self.pod)
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)
elif type == 'erase':
reqQuery = '{0}/market/delete'.format(self.pod)
result = requests.get(reqQuery, headers=headers)
# print(result)
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 sendDocumentSet(self, document, type, id=None):
headers = {
'Content-type': 'application/json',
}
# Send JSON document and get JSON result
if type == 'set':
reqQuery = '{0}/market/record'.format(self.pod)
if type == 'delete':
reqQuery = '{0}/market/record/{1}/_update'.format(self.pod, id)
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)['_source']
if doc:
# pubkey = { "pubkey": doc['issuer'] }
# rest = { "description": doc['description'] }
# final = {**pubkey, **rest}
return json.dumps(doc, indent=2)
else:
return 'Profile vide'

138
lib/profiles.py Normal file → Executable file
View File

@ -1,4 +1,4 @@
import sys, re, json, requests, base64
import sys, json, requests, base64
from time import time
from lib.cesiumCommon import CesiumCommon, PUBKEY_REGEX
@ -9,71 +9,74 @@ class Profiles(CesiumCommon):
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:
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
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
data["socials"] = []
data["socials"].append({})
data["socials"][0]["type"] = "web"
data["socials"][0]["url"] = socials
if avatar:
avatar = open(avatar, 'rb').read()
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'] = []
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)
document = json.dumps(data)
return self.signDoc(document)
# Configure JSON document GET to send
def configDocGet(self, profile, scope='title', getAvatar=None):
# 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": {
"query": {
"bool": {
"should":[
{
"match":{
scope:{
"query": profile,"boost":2
}
}
},{
"prefix": {scope: profile}
}
"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
}
},
"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)
document = json.dumps(data)
return document
@ -82,44 +85,45 @@ class Profiles(CesiumCommon):
timeSent = int(time())
data = {}
data['time'] = timeSent
data['id'] = self.pubkey
data['issuer'] = self.pubkey
data['version'] = 2
data['index'] = "user"
data['type'] = "profile"
data["time"] = timeSent
data["id"] = self.pubkey
data["issuer"] = self.pubkey
data["version"] = 2
data["index"] = "user"
data["type"] = "profile"
document = json.dumps(data)
document = json.dumps(data)
return self.signDoc(document)
def sendDocument(self, document, type):
headers = {
'Content-type': 'application/json',
"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)
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')
sys.stderr.write("Echec de l'envoi du document...\n" + result.text + "\n")
def parseJSON(self, doc):
doc = json.loads(doc)['hits']['hits']
doc = json.loads(doc)["hits"]["hits"]
if doc:
pubkey = { "pubkey": doc[0]['_id'] }
rest = doc[0]['_source']
pubkey = {"pubkey": doc[0]["_id"]}
rest = doc[0]["_source"]
final = {**pubkey, **rest}
return json.dumps(final, indent=2)
else:
return 'Profile vide'
return "Profile vide"

86
lib/qrcode-reader.py Executable file
View File

@ -0,0 +1,86 @@
#!/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)

0
lib/likes.py → lib/stars.py Normal file → Executable file
View File

2
requirements.txt Normal file → Executable file
View File

@ -4,3 +4,5 @@ pybase64
duniterpy
termcolor
python-dotenv
gql
requests

View File

@ -1,5 +1,7 @@
#!/bin/bash
hasError=0
for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel; do
if [ $(dpkg-query -W -f='${Status}' $i 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
[[ ! $j ]] && sudo apt update
@ -8,5 +10,12 @@ for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel;
fi
done
pip3 install -r requirements.txt
pip3 install -r requirements.txt || hasError=1
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