added handling of [trusted] and [note] to tn-cli and macros

This commit is contained in:
or-else
2021-08-14 18:55:26 -07:00
parent fe0fa704bf
commit 9eba3cbaea
3 changed files with 76 additions and 23 deletions

View File

@ -50,6 +50,7 @@ python tn-cli.py < sample-script.txt
### Local (non-networking)
* `.await` - issue a gRPC call and wait for completion, optionally assign result to a variable.
* `.delmark` - use custom delete marker instead of default `DEL!`; needed when some value is to be removed rather than set to blank.
* `.exit` - terminate execution and exit the CLI; also `.quit`.
* `.log` - write a value of a variable to `stdout`.
* `.must` - issue a gRPC call and wait for completion, optionally assign result to a variable; raise an exception if result is not a success.
@ -85,7 +86,7 @@ Macros are high-level wrappers for series of gRPC calls. Currently, the followin
* `useradd` - create a new user account
* `userdel` - delete user account (requires root privileges)
* `usermod` - modify user account (requires root privileges)
* `vcard` - print user's public and private info (requires root privileges)
* `thecard` - print user's public and private info (requires root privileges)
You can define your own macros in [macros.py](macros.py) or create a separate python module (you can load it via `--load-macros`).
Refer to [macros.py](macros.py) for examples.

View File

@ -15,7 +15,7 @@ class Macro:
self.parser = argparse.ArgumentParser(prog=self.name(), description=self.description())
self.add_parser_args()
# Explain argument.
self.parser.add_argument('--explain', action='store_true', help='Only print out expanded macro')
self.parser.add_argument('--explain', action='store_true', help='Only print out expanded macro')
def name(self):
"""Macro name."""
pass
@ -65,7 +65,9 @@ class Usermod(Macro):
self.parser.add_argument('-U', '--unsuspend', action='store_true', help='Unsuspend account')
self.parser.add_argument('--name', help='Public name')
self.parser.add_argument('--avatar', help='Avatar file name')
self.parser.add_argument('--comment', help='Private comment on account')
self.parser.add_argument('--comment', help='Private comment on account')
self.parser.add_argument('--note', help='Account description')
self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger')
def expand(self, id, cmd, args):
if not cmd.userid:
@ -91,6 +93,10 @@ class Usermod(Macro):
set_cmd += ' --photo="%s"' % cmd.avatar
if cmd.comment is not None:
set_cmd += ' --private="%s"' % cmd.comment
if cmd.note is not None:
set_cmd += ' --note="%s"' % cmd.note
if cmd.trusted is not None:
set_cmd += ' --trusted="%s"' % cmd.trusted
old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else ''
return ['.use --user %s' % cmd.userid,
'.must sub me',
@ -162,6 +168,8 @@ class Useradd(Macro):
self.parser.add_argument('--cred', help='List of comma-separated credentials in format "(email|tel):value1,(email|tel):value2,..."')
self.parser.add_argument('--name', help='Public name of the user')
self.parser.add_argument('--comment', help='Private comment')
self.parser.add_argument('--note', help='Public description')
self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger')
self.parser.add_argument('--tags', help='Comma-separated list of tags')
self.parser.add_argument('--avatar', help='Path to avatar file')
self.parser.add_argument('--auth', help='Default auth acs')
@ -182,6 +190,10 @@ class Useradd(Macro):
new_cmd += ' --fn="%s"' % cmd.name
if cmd.comment:
new_cmd += ' --private="%s"' % cmd.comment
if cmd.note is not None:
set_cmd += ' --note="%s"' % cmd.note
if cmd.trusted is not None:
set_cmd += ' --trusted="%s"' % cmd.trusted
if cmd.tags:
new_cmd += ' --tags="%s"' % cmd.tags
if cmd.avatar:
@ -287,8 +299,8 @@ class Chcred(Macro):
'.use --user "%s"' % old_user]
class VCard(Macro):
"""Prints user's VCard."""
class Thecard(Macro):
"""Prints user's theCard."""
def name(self):
return "vcard"
@ -324,4 +336,4 @@ def parse_macro(parts):
return macro.parser
Macros = {x.name(): x for x in [Usermod(), Resolve(), Passwd(), Useradd(), Chacs(), Userdel(), Chcred(), VCard()]}
Macros = {x.name(): x for x in [Usermod(), Resolve(), Passwd(), Useradd(), Chacs(), Userdel(), Chcred(), Thecard()]}

View File

@ -78,6 +78,12 @@ RE_INDEX = re.compile(r"(\w+)\[(\w+)\]")
# Macros module (may be None).
macros = None
# String used as a delete marker. I.e. when a value needs to be deleted, use this string
DELETE_MARKER = 'DEL!'
# Unicode DEL character used internally by Tinode when a value needs to be deleted.
TINODE_DEL = ''
# Python is retarded.
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
@ -86,20 +92,25 @@ class dotdict(dict):
__delattr__ = dict.__delitem__
# Pack user's name and avatar into a theCard.
def makeTheCard(fn, photofile):
# Pack name, description, and avatar into a theCard.
def makeTheCard(fn, note, photofile):
card = None
if (fn != None and fn.strip() != "") or photofile != None:
if (fn != None and fn.strip() != "") or photofile != None or note != None:
card = {}
if fn != None:
card['fn'] = fn.strip()
fn = fn.strip()
card['fn'] = TINODE_DEL if fn == DELETE_MARKER or fn == '' else fn
if note != None:
note = note.strip()
card['note'] = TINODE_DEL if note == DELETE_MARKER or note == '' else note
if photofile != None:
if photofile == '':
if photofile == '' or photofile == DELETE_MARKER:
# Delete the avatar.
card['photo'] = {
'data': ''
'data': TINODE_DEL
}
else:
try:
@ -203,6 +214,20 @@ def parse_cred(cred):
return result
# Parse trusted values: [staff,rm-verified].
def parse_trusted(trusted):
result = None
if trusted != None:
result = {}
for t in trusted.split(","):
t = t.strip()
if t.startswith("rm-"):
result[t[3:]] = TINODE_DEL
else:
result[t] = True
return result
# Read a value in the server response using dot notation, i.e.
# $user.params.token or $meta.sub[1].user
def getVar(path):
@ -326,12 +351,12 @@ def accMsg(id, cmd, ignored):
elif cmd.suspend == 'false':
state = 'ok'
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.photo))
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.note, cmd.photo))
cmd.private = encode_to_bytes(cmd.private)
return pb.ClientMsg(acc=pb.ClientAcc(id=str(id), user_id=cmd.user, state=state,
scheme=cmd.scheme, secret=cmd.secret, login=cmd.do_login, tags=cmd.tags.split(",") if cmd.tags else None,
desc=pb.SetDesc(default_acs=pb.DefaultAcsMode(auth=cmd.auth, anon=cmd.anon),
public=cmd.public, private=cmd.private),
public=cmd.public, private=cmd.private, trusted=parse_trusted(cmd.trusted)),
cred=parse_cred(cmd.cred)),
extra=pb.ClientExtra(on_behalf_of=tn_globals.DefaultUser))
@ -367,11 +392,11 @@ def subMsg(id, cmd, ignored):
cmd.topic = tn_globals.DefaultTopic
if cmd.get_query:
cmd.get_query = pb.GetQuery(what=" ".join(cmd.get_query.split(",")))
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.photo))
cmd.private = encode_to_bytes(cmd.private)
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.note, cmd.photo))
cmd.private = TINODE_DEL if cmd.private == DELETE_MARKER else encode_to_bytes(cmd.private)
return pb.ClientMsg(sub=pb.ClientSub(id=str(id), topic=cmd.topic,
set_query=pb.SetQuery(
desc=pb.SetDesc(public=cmd.public, private=cmd.private,
desc=pb.SetDesc(public=cmd.public, private=cmd.private, trusted=parse_trusted(cmd.trusted),
default_acs=pb.DefaultAcsMode(auth=cmd.auth, anon=cmd.anon)),
sub=pb.SetSub(mode=cmd.mode),
tags=cmd.tags.split(",") if cmd.tags else None),
@ -438,10 +463,10 @@ def setMsg(id, cmd, ignored):
cmd.topic = tn_globals.DefaultTopic
if cmd.public == None:
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.photo))
cmd.public = encode_to_bytes(makeTheCard(cmd.fn, cmd.note, cmd.photo))
else:
cmd.public = encode_to_bytes(cmd.public)
cmd.private = encode_to_bytes(cmd.private)
cmd.public = TINODE_DEL if cmd.public == DELETE_MARKER else encode_to_bytes(cmd.public)
cmd.private = TINODE_DEL if cmd.private == DELETE_MARKER else encode_to_bytes(cmd.private)
cred = parse_cred(cmd.cred)
if cred:
if len(cred) > 1:
@ -451,7 +476,7 @@ def setMsg(id, cmd, ignored):
return pb.ClientMsg(set=pb.ClientSet(id=str(id), topic=cmd.topic,
query=pb.SetQuery(
desc=pb.SetDesc(default_acs=pb.DefaultAcsMode(auth=cmd.auth, anon=cmd.anon),
public=cmd.public, private=cmd.private),
public=cmd.public, private=cmd.private, trusted=parse_trusted(cmd.trusted)),
sub=pb.SetSub(user_id=cmd.user, mode=cmd.mode),
tags=cmd.tags.split(",") if cmd.tags else None,
cred=cred)),
@ -603,7 +628,6 @@ def upload(id, cmd, args):
return None
# Given an array of parts, parse commands and arguments
def parse_cmd(parts):
parser = None
@ -619,6 +643,8 @@ def parse_cmd(parts):
parser.add_argument('--fn', default=None, help='user\'s human name')
parser.add_argument('--photo', default=None, help='avatar file name')
parser.add_argument('--private', default=None, help='user\'s private info')
parser.add_argument('--note', default=None, help='user\'s description')
parser.add_argument('--trusted', default=None, help='trusted markers: verified, staff, danger, prepend with rm- to remove, e.g. rm-verified')
parser.add_argument('--auth', default=None, help='default access mode for authenticated users')
parser.add_argument('--anon', default=None, help='default access mode for anonymous users')
parser.add_argument('--cred', default=None, help='credentials, comma separated list in method:value format, e.g. email:test@example.com,tel:12345')
@ -646,6 +672,8 @@ def parse_cmd(parts):
parser.add_argument('--fn', default=None, help='topic\'s user-visible name')
parser.add_argument('--photo', default=None, help='avatar file name')
parser.add_argument('--private', default=None, help='topic\'s private info')
parser.add_argument('--note', default=None, help='topic\'s description')
parser.add_argument('--trusted', default=None, help='trusted markers: verified, staff, danger')
parser.add_argument('--auth', default=None, help='default access mode for authenticated users')
parser.add_argument('--anon', default=None, help='default access mode for anonymous users')
parser.add_argument('--mode', default=None, help='new value of access mode')
@ -680,8 +708,10 @@ def parse_cmd(parts):
parser.add_argument('topic', help='topic to update')
parser.add_argument('--fn', help='topic\'s title')
parser.add_argument('--photo', help='avatar file name')
parser.add_argument('--public', help='topic\'s public info, alternative to fn+photo')
parser.add_argument('--public', help='topic\'s public info, alternative to fn+photo+note')
parser.add_argument('--private', help='topic\'s private info')
parser.add_argument('--note', default=None, help='topic\'s description')
parser.add_argument('--trusted', default=None, help='trusted markers: verified, staff, danger')
parser.add_argument('--auth', help='default access mode for authenticated users')
parser.add_argument('--anon', help='default access mode for anonymous users')
parser.add_argument('--user', help='ID of the account to update')
@ -748,6 +778,10 @@ def parse_input(cmd):
elif parts[0] == ".verbose":
parser = argparse.ArgumentParser(prog=parts[0], description='Toggle logging verbosity')
elif parts[0] == ".delmark":
parser = argparse.ArgumentParser(prog=parts[0], description='Use custom delete maker instead of default DEL!')
parser.add_argument('delmark', help='marker to use')
else:
parser = parse_cmd(parts)
@ -755,6 +789,7 @@ def parse_input(cmd):
printout("Unrecognized:", parts[0])
printout("Possible commands:")
printout("\t.await\t\t- wait for completion of an operation")
printout("\t.delmark\t\t- custom delete marker to use instead of default DEL!")
printout("\t.exit\t\t- exit the program (also .quit)")
printout("\t.log\t\t- write value of a variable to stdout")
printout("\t.must\t\t- wait for completion of an operation, terminate on failure")
@ -852,6 +887,11 @@ def serialize_cmd(string, id, args):
stdoutln("Logging is {}".format("verbose" if tn_globals.Verbose else "normal"))
return None, None
elif cmd.cmd == ".delmark":
DELETE_MARKER = cmd.delmark
stdoutln("Using {} as delete marker".format(DELETE_MARKER))
return None, None
elif cmd.cmd == "upload":
# Start async upload
upload_thread = threading.Thread(target=upload, args=(id, derefVals(cmd), args), name="Uploader_"+cmd.filename)