From 6bfd4e7446a278246aece0c0c2cef638b9cd23f5 Mon Sep 17 00:00:00 2001 From: n9k Date: Sat, 25 Jun 2022 03:18:57 +0000 Subject: [PATCH] Allowedness: change from control socket --- anonstream/control/parse.py | 2 + anonstream/control/spec/common.py | 39 ++++++- .../control/spec/methods/allowedness.py | 105 ++++++++++++++++++ anonstream/control/spec/methods/help.py | 5 +- 4 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 anonstream/control/spec/methods/allowedness.py diff --git a/anonstream/control/parse.py b/anonstream/control/parse.py index f456b3d..a567c6d 100644 --- a/anonstream/control/parse.py +++ b/anonstream/control/parse.py @@ -3,6 +3,7 @@ from anonstream.control.spec import ParseException, Parsed from anonstream.control.spec.common import Str +from anonstream.control.spec.methods.allowedness import SPEC as SPEC_ALLOWEDNESS from anonstream.control.spec.methods.chat import SPEC as SPEC_CHAT from anonstream.control.spec.methods.exit import SPEC as SPEC_EXIT from anonstream.control.spec.methods.help import SPEC as SPEC_HELP @@ -15,6 +16,7 @@ SPEC = Str({ 'title': SPEC_TITLE, 'chat': SPEC_CHAT, 'user': SPEC_USER, + 'allowednesss': SPEC_ALLOWEDNESS, }) async def parse(request): diff --git a/anonstream/control/spec/common.py b/anonstream/control/spec/common.py index 7091dc0..bfee339 100644 --- a/anonstream/control/spec/common.py +++ b/anonstream/control/spec/common.py @@ -7,6 +7,8 @@ from anonstream.control.spec import Spec, NoParse, Ambiguous, Parsed from anonstream.control.spec.utils import get_item, startswith class Str(Spec): + AS_ARG = False + def __init__(self, directives): self.directives = directives @@ -33,7 +35,10 @@ class Str(Spec): f'bad word at position {index} {word!r}: ambiguous ' f'abbreviation: {set(candidates)}' ) - return self.directives[directive], 1, [] + args = [] + if self.AS_ARG: + args.append(directive) + return self.directives[directive], 1, args class End(Spec): def __init__(self, fn): @@ -48,6 +53,9 @@ class Args(Spec): def __init__(self, spec): self.spec = spec +class ArgsStr(Str): + AS_ARG = True + class ArgsInt(Args): def consume(self, words, index): try: @@ -102,6 +110,14 @@ class ArgsJson(Args): obj = self.transform_obj(obj) return self.spec, 1, [obj] +class ArgsJsonBoolean(ArgsJson): + def assert_obj(self, index, obj_json, obj): + if not isinstance(obj, bool): + raise NoParse( + f'bad argument at position {index} {obj_json!r}: ' + f'could not decode json boolean' + ) + class ArgsJsonString(ArgsJson): def assert_obj(self, index, obj_json, obj): if not isinstance(obj, str): @@ -109,3 +125,24 @@ class ArgsJsonString(ArgsJson): f'bad argument at position {index} {obj_json!r}: ' f'could not decode json string' ) + +class ArgsJsonArray(ArgsJson): + def assert_obj(self, index, obj_json, obj): + if not isinstance(obj, list): + raise NoParse( + f'bad argument at position {index} {obj_json!r}: ' + f'could not decode json array' + ) + +class ArgsJsonStringArray(ArgsJson): + def assert_obj(self, index, obj_json, obj): + if not isinstance(obj, list): + raise NoParse( + f'bad argument at position {index} {obj_json!r}: ' + f'could not decode json array' + ) + if any(not isinstance(item, str) for item in obj): + raise NoParse( + f'bad argument at position {index} {obj_json!r}: ' + f'could not decode json array of strings' + ) diff --git a/anonstream/control/spec/methods/allowedness.py b/anonstream/control/spec/methods/allowedness.py new file mode 100644 index 0000000..acc4284 --- /dev/null +++ b/anonstream/control/spec/methods/allowedness.py @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: 2022 n9k +# SPDX-License-Identifier: AGPL-3.0-or-later + +import json + +from quart import current_app + +from anonstream.control.exceptions import CommandFailed +from anonstream.control.spec.common import Str, End, ArgsStr, ArgsJsonBoolean, ArgsJsonString, ArgsJsonStringArray +from anonstream.control.spec.utils import json_dumps_contiguous + +ALLOWEDNESS = current_app.allowedness + +async def cmd_allowedness_help(): + normal = ['allowedness', 'help'] + response = ( + 'Usage: allowedness [show | set default BOOLEAN | add LIST KEYTUPLE VALUE | remove LIST KEYTUPLE VALUE]\n' + 'Commands:\n' + ' allowedness [show]........................\n' + ' allowedness setdefault BOOLEAN............set the default allowedness\n' + #' allowedness clear LIST all................\n' + #' allowedness clear LIST keytuple KEYTUPLE..\n' + ' allowedness add LIST KEYTUPLE STRING......add to the blacklist/whitelist\n' + ' allowedness remove LIST KEYTUPLE STRING...remove from the blacklist/whitelist\n' + 'Definitions:\n' + ' BOOLEAN...................................={true | false}\n' + ' LIST......................................={blacklist | whitelist}\n' + ' KEYTUPLE..................................keys to burrow into a user with, e.g. (\'tripcode\', \'digest\'), encoded as a json array\n' + ' STRING....................................a json string\n' + ) + return normal, response + +async def cmd_allowedness_show(): + allowedness_for_json = { + 'blacklist': {}, + 'whitelist': {}, + 'default': ALLOWEDNESS['default'], + } + for colourlist in ('blacklist', 'whitelist'): + for keytuple, values in ALLOWEDNESS[colourlist].items(): + allowedness_for_json[colourlist]['.'.join(keytuple)] = sorted(values) + normal = ['allowedness', 'show'] + response = json.dumps(allowedness_for_json) + '\n' + return normal, response + +async def cmd_allowedness_setdefault(value): + ALLOWEDNESS['default'] = value + normal = ['allowednesss', 'setdefault', json_dumps_contiguous(value)] + response = '' + return normal, response + +async def cmd_allowedness_add(colourlist, keytuple_list, value): + keytuple = tuple(keytuple_list) + try: + values = ALLOWEDNESS[colourlist][keytuple] + except KeyError: + raise CommandFailed(f'no such keytuple {keytuple!r} in list {colourlist!r}') + else: + values.add(value) + normal = [ + 'allowednesss', + 'add', + colourlist, + json_dumps_contiguous(keytuple), + json_dumps_contiguous(value), + ] + response = '' + return normal, response + +async def cmd_allowedness_remove(colourlist, keytuple_list, value): + keytuple = tuple(keytuple_list) + try: + values = ALLOWEDNESS[colourlist][keytuple] + except KeyError: + raise CommandFailed(f'no such keytuple {keytuple!r} in list {colourlist!r}') + else: + try: + values.remove(value) + except KeyError: + pass + normal = [ + 'allowednesss', + 'remove', + colourlist, + json_dumps_contiguous(keytuple), + json_dumps_contiguous(value), + ] + response = '' + return normal, response + +SPEC = Str({ + None: End(cmd_allowedness_show), + 'help': End(cmd_allowedness_help), + 'show': End(cmd_allowedness_show), + 'setdefault': ArgsJsonBoolean(End(cmd_allowedness_setdefault)), + #'clear': cmd_allowedness_clear, + 'add': ArgsStr({ + 'blacklist': ArgsJsonStringArray(ArgsJsonString(End(cmd_allowedness_add))), + 'whitelist': ArgsJsonStringArray(ArgsJsonString(End(cmd_allowedness_add))), + }), + 'remove': ArgsStr({ + 'blacklist': ArgsJsonStringArray(ArgsJsonString(End(cmd_allowedness_remove))), + 'whitelist': ArgsJsonStringArray(ArgsJsonString(End(cmd_allowedness_remove))), + }), +}) diff --git a/anonstream/control/spec/methods/help.py b/anonstream/control/spec/methods/help.py index 03a118f..8a5399f 100644 --- a/anonstream/control/spec/methods/help.py +++ b/anonstream/control/spec/methods/help.py @@ -16,11 +16,14 @@ async def cmd_help(): ' user attr USER.................set an attribute of a user\n' ' user get USER ATTR.............set an attribute of a user\n' ' user set USER ATTR VALUE.......set an attribute of a user\n' - #' user kick USERS [FAREWELL].....kick users\n' ' user eyes USER [show]..........show a list of active video responses\n' ' user eyes USER delete EYES_ID..end a video response\n' #' chat show MESSAGES.............show a list of messages\n' ' chat delete SEQS...............delete a set of messages\n' + ' allowedness [show].............show the current allowedness\n' + ' allowedness setdefault BOOLEAN.set the default allowedness\n' + ' allowedness add SET STRING.....add to the blacklist/whitelist\n' + ' allowedness remove SET STRING..remove from the blacklist/whitelist\n' ) return normal, response