refacto arguments parsing

This commit is contained in:
poka 2023-09-10 08:15:56 +02:00
parent 919a4171f5
commit 5fe53c1d4c
1 changed files with 323 additions and 252 deletions

575
jaklis.py
View File

@ -5,31 +5,39 @@ import sys
import os
import string
import random
from os.path import join, dirname
from shutil import copyfile
from dotenv import load_dotenv
from duniterpy.key import SigningKey
from pathlib import Path
from lib.gva import GvaApi
from lib.cesium import CesiumPlus
__version__ = "0.1.0"
__version__ = "0.1.1"
MY_PATH = os.path.realpath(sys.argv[0]).replace("jaklis.py", "")
MY_PATH = Path(__file__).resolve().parent
# Get environment variables
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"
# Check and create dotenv file
if not dotenv_file.is_file():
dotenv_file.write_text(dotenv_template.read_text())
# Load environment variables
load_dotenv(dotenv_file)
# 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
# Parse arguments
# define parser
parser = argparse.ArgumentParser(
description="CLI Client for Cesium+ and Ḡchange",
epilog="current node: '" + node + "', current pod: '" + pod + "'.",
)
# load global arguments
parser.add_argument(
"-v",
"--version",
@ -41,175 +49,231 @@ parser.add_argument(
"-n", "--node", help="Address of the Cesium+, Gchange, or Duniter node to use"
)
# Create subparsers for different commands
# 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"},
},
},
"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",
},
},
},
"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",
},
},
},
"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",
},
},
},
"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",
},
},
},
"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"},
},
},
"erase": {"help": "Erase your Cesium+ profile", "arguments": {}},
"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"},
},
},
"unstars": {
"help": "Remove a star",
"arguments": {
("p", "profile"): {"help": "Profile to unstar"},
},
},
"getoffer": {
"help": "Get information about a Ḡchange listing",
"arguments": {
("i", "id"): {"help": "Target listing to retrieve"},
},
},
"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"},
},
},
"deleteoffer": {
"help": "Delete a Ḡchange listing",
"arguments": {
("i", "id"): {"help": "Target listing to delete"},
},
},
"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",
},
},
},
"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",
},
},
},
"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",
},
},
},
"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"},
},
},
"idBalance": {
"help": "View public key/username identity and balance",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
},
},
"currentUd": {
"help": "Display the current Universal Dividend amount",
"arguments": {
("p", "pubkey"): {"help": "Public key of the target account"},
},
},
"listWallets": {
"help": "List all G1 wallets",
"arguments": {
("b", "balance"): {"action": "store_true", "help": "Display balances"},
("mbr"): {
"action": "store_true",
"help": "Display raw list of member pubkeys",
},
("non_mbr"): {
"action": "store_true",
"help": "Display raw list of nonmember identity pubkeys",
},
("larf"): {
"action": "store_true",
"help": "Display raw list of nonmember pubkeys",
},
("brut"): {
"action": "store_true",
"help": "Display raw list of all pubkeys",
},
("p", "pubkey"): {"help": "Useless but needed"},
},
},
"geolocProfiles": {"help": "Get JSON of all geolocated accounts", "arguments": {}},
}
# Process commands and arguments
subparsers = parser.add_subparsers(title="jaklis Commands", dest="cmd")
read_cmd = subparsers.add_parser("read", help="Read messages")
send_cmd = subparsers.add_parser("send", help="Send a message")
delete_cmd = subparsers.add_parser("delete", help="Delete a message")
getProfile_cmd = subparsers.add_parser("get", help="View a Cesium+ profile")
getPage_cmd = subparsers.add_parser("page", help="View a Cesium+ page")
setProfile_cmd = subparsers.add_parser("set", help="Configure your Cesium+ profile")
eraseProfile_cmd = subparsers.add_parser("erase", help="Erase your Cesium+ profile")
stars_cmd = subparsers.add_parser(
"stars", help="View a profile's stars / Rate a profile (option -s RATING)"
)
unstars_cmd = subparsers.add_parser("unstars", help="Remove a star")
getoffer_cmd = subparsers.add_parser(
"getoffer", help="Get information about a Ḡchange listing"
)
setoffer_cmd = subparsers.add_parser("setoffer", help="Create a Ḡchange listing")
deleteoffer_cmd = subparsers.add_parser("deleteoffer", help="Delete a Ḡchange listing")
pay_cmd = subparsers.add_parser("pay", help="Pay in Ḡ1")
history_cmd = subparsers.add_parser(
"history", help="View Ḡ1 account transaction history"
)
balance_cmd = subparsers.add_parser("balance", help="View Ḡ1 account balance")
id_cmd = subparsers.add_parser("id", help="View public key/username identity")
id_balance_cmd = subparsers.add_parser(
"idBalance", help="View public key/username identity and balance"
)
currentUd = subparsers.add_parser(
"currentUd", help="Display the current Universal Dividend amount"
)
listWallets = subparsers.add_parser("listWallets", help="List all G1 wallets")
geolocProfiles = subparsers.add_parser(
"geolocProfiles", help="Get JSON of all geolocated accounts"
)
# Messaging management commands
read_cmd.add_argument(
"-n", "--number", type=int, default=3, help="Display the last NUMBER messages"
)
read_cmd.add_argument("-j", "--json", action="store_true", help="Output in JSON format")
read_cmd.add_argument("-o", "--outbox", action="store_true", help="Read sent messages")
send_cmd.add_argument(
"-d", "--destinataire", required=True, help="Recipient of the message"
)
send_cmd.add_argument("-t", "--titre", help="Title of the message to send")
send_cmd.add_argument("-m", "--message", help="Message to send")
send_cmd.add_argument("-f", "--fichier", help="Send the message from the 'FILE'")
send_cmd.add_argument(
"-o", "--outbox", action="store_true", help="Send the message to the outbox"
)
delete_cmd.add_argument(
"-i",
"--id",
action="append",
nargs="+",
required=True,
help="ID(s) of the message(s) to delete",
)
delete_cmd.add_argument(
"-o", "--outbox", action="store_true", help="Delete a sent message"
)
# Profile management commands
setProfile_cmd.add_argument("-n", "--name", help="Profile name")
setProfile_cmd.add_argument("-d", "--description", help="Profile description")
setProfile_cmd.add_argument("-v", "--ville", help="Profile city")
setProfile_cmd.add_argument("-a", "--adresse", help="Profile address")
setProfile_cmd.add_argument(
"-pos", "--position", nargs=2, help="Geographical coordinates (lat + lon)"
)
setProfile_cmd.add_argument("-s", "--site", help="Profile website")
setProfile_cmd.add_argument("-A", "--avatar", help="Path to profile avatar in PNG")
getProfile_cmd.add_argument("-p", "--profile", help="Profile name")
getProfile_cmd.add_argument(
"-a",
"--avatar",
action="store_true",
help="Also retrieve the avatar in raw base64 format",
)
getPage_cmd.add_argument("-p", "--page", help="Page name")
getPage_cmd.add_argument(
"-a",
"--avatar",
action="store_true",
help="Also retrieve the page's avatar in raw base64 format",
)
# Likes management commands
stars_cmd.add_argument("-p", "--profile", help="Target profile")
stars_cmd.add_argument("-n", "--number", type=int, help="Number of stars")
unstars_cmd.add_argument("-p", "--profile", help="Profile to unstar")
# Offers management commands
getoffer_cmd.add_argument("-i", "--id", help="Target listing to retrieve")
setoffer_cmd.add_argument("-t", "--title", help="Title of the listing to create")
setoffer_cmd.add_argument(
"-d", "--description", help="Description of the listing to create"
)
setoffer_cmd.add_argument("-c", "--category", help="Category of the listing to create")
setoffer_cmd.add_argument(
"-l",
"--localisation",
nargs=2,
help="Location of the listing to create (lat + lon)",
)
setoffer_cmd.add_argument("-p", "--picture", help="Image of the listing to create")
setoffer_cmd.add_argument("-ci", "--city", help="City of the listing to create")
setoffer_cmd.add_argument("-pr", "--price", help="Price of the listing to create")
deleteoffer_cmd.add_argument("-i", "--id", help="Target listing to delete")
# GVA usage commands
pay_cmd.add_argument("-p", "--pubkey", help="Payment recipient")
pay_cmd.add_argument("-a", "--amount", type=float, help="Transaction amount")
pay_cmd.add_argument(
"-c", "--comment", default="", help="Transaction comment", nargs="*"
)
pay_cmd.add_argument("-m", "--mempool", action="store_true", help="Use mempool sources")
pay_cmd.add_argument(
"-v",
"--verbose",
action="store_true",
help="Display the JSON result of the transaction",
)
history_cmd.add_argument("-p", "--pubkey", help="Public key of the target account")
history_cmd.add_argument(
"-n",
"--number",
type=int,
default=10,
help="Display the last NUMBER transactions",
)
history_cmd.add_argument(
"-j", "--json", action="store_true", help="Display the result in JSON format"
)
history_cmd.add_argument(
"--nocolors", action="store_true", help="Display the result in black and white"
)
balance_cmd.add_argument("-p", "--pubkey", help="Public key of the target account")
balance_cmd.add_argument(
"-m", "--mempool", action="store_true", help="Use mempool sources"
)
id_cmd.add_argument("-p", "--pubkey", help="Public key of the target account")
id_cmd.add_argument("-u", "--username", help="Username of the target account")
id_balance_cmd.add_argument("-p", "--pubkey", help="Public key of the target account")
currentUd.add_argument("-p", "--pubkey", help="Public key of the target account")
listWallets.add_argument(
"-b", "--balance", action="store_true", help="Display balances"
)
listWallets.add_argument(
"--mbr", action="store_true", help="Display raw list of member pubkeys"
)
listWallets.add_argument(
"--non_mbr",
action="store_true",
help="Display raw list of non-member identity pubkeys",
)
listWallets.add_argument(
"--larf", action="store_true", help="Display raw list of non-member pubkeys"
)
listWallets.add_argument(
"--brut", action="store_true", help="Display raw list of all pubkeys"
)
listWallets.add_argument("-p", "--pubkey", help="Useless but needed")
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()
cmd = args.cmd
@ -220,7 +284,7 @@ if args.version:
if not cmd:
parser.print_help()
sys.exit(1)
sys.exit(0)
def createTmpDunikey():
@ -239,73 +303,50 @@ def createTmpDunikey():
return keyPath
# Check if a dunikey is needed
try:
pubkey = args.pubkey
except:
pubkey = False
try:
profile = args.profile
except:
profile = False
if cmd in (
"history",
"balance",
"get",
"page",
"id",
"idBalance",
"listWallets",
) and (pubkey or profile):
noNeedDunikey = True
keyPath = False
def get_arg_value(args, arg):
try:
dunikey = args.pubkey
except:
dunikey = args.profile
else:
noNeedDunikey = False
return getattr(args, arg)
except AttributeError:
return False
def get_dunikey(args):
if args.key:
dunikey = args.key
keyPath = False
else:
dunikey = os.getenv("DUNIKEY")
if not dunikey:
keyPath = createTmpDunikey()
dunikey = keyPath
else:
keyPath = False
return args.key
dunikey = os.getenv("DUNIKEY")
if not dunikey:
keyPath = createTmpDunikey()
dunikey = keyPath
if not os.path.isfile(dunikey):
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 the CesiumPlus object
if cmd in (
"read",
"send",
"delete",
"set",
"get",
pubkey = get_arg_value(args, "pubkey")
profile = get_arg_value(args, "profile")
noNeedDunikey = cmd in (
"history",
"balance",
"page",
"erase",
"stars",
"unstars",
"getoffer",
"setoffer",
"deleteoffer",
"geolocProfiles",
):
from lib.cesium import CesiumPlus
"id",
"idBalance",
"listWallets",
) and (pubkey or profile)
if args.node:
pod = args.node
if noNeedDunikey:
dunikey = pubkey if pubkey else profile
else:
dunikey = get_dunikey(args)
cesium = CesiumPlus(dunikey, pod, noNeedDunikey)
keyPath = False if dunikey else createTmpDunikey()
def handle_cesium_commands(args, cmd, cesium):
# Messaging
if cmd == "read":
cesium.read(args.number, args.outbox, args.json)
@ -374,27 +415,11 @@ if cmd in (
)
elif cmd == "deleteoffer":
cesium.deleteOffer(args.id)
else:
raise ValueError(f"Unknown command: {cmd}")
# Construct the GvaApi object
elif cmd in (
"pay",
"history",
"balance",
"id",
"idBalance",
"currentUd",
"listWallets",
):
from lib.gva import GvaApi
if args.node:
node = args.node
if args.pubkey:
destPubkey = args.pubkey
gva = GvaApi(dunikey, node, destPubkey, noNeedDunikey)
def handle_gva_commands(args, cmd, gva):
if cmd == "pay":
gva.pay(args.amount, args.comment, args.mempool, args.verbose)
elif cmd == "history":
@ -409,6 +434,52 @@ elif cmd in (
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 cmd in (
"read",
"send",
"delete",
"set",
"get",
"page",
"erase",
"stars",
"unstars",
"getoffer",
"setoffer",
"deleteoffer",
"geolocProfiles",
):
if args.node:
pod = args.node
cesium = CesiumPlus(dunikey, pod, noNeedDunikey)
handle_cesium_commands(args, cmd, cesium)
# Construct the GvaApi object
elif cmd in (
"pay",
"history",
"balance",
"id",
"idBalance",
"currentUd",
"listWallets",
):
if args.node:
node = args.node
if 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)