diff --git a/anonstream/control/parse.py b/anonstream/control/parse.py index de30db3..7acf77f 100644 --- a/anonstream/control/parse.py +++ b/anonstream/control/parse.py @@ -5,6 +5,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.emote import SPEC as SPEC_EMOTE from anonstream.control.spec.methods.help import SPEC as SPEC_HELP from anonstream.control.spec.methods.quit import SPEC as SPEC_QUIT from anonstream.control.spec.methods.title import SPEC as SPEC_TITLE @@ -17,6 +18,7 @@ SPEC = Str({ 'chat': SPEC_CHAT, 'user': SPEC_USER, 'allowednesss': SPEC_ALLOWEDNESS, + 'emote': SPEC_EMOTE, }) async def parse(request): diff --git a/anonstream/control/spec/methods/emote.py b/anonstream/control/spec/methods/emote.py new file mode 100644 index 0000000..8318ad4 --- /dev/null +++ b/anonstream/control/spec/methods/emote.py @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2022 n9k +# SPDX-License-Identifier: AGPL-3.0-or-later + +import json + +from quart import current_app + +from anonstream.emote import load_emote_schema_async, BadEmote +from anonstream.helpers.emote import get_emote_markup +from anonstream.control.spec.common import Str, End +from anonstream.control.exceptions import CommandFailed + +CONFIG = current_app.config +EMOTES = current_app.emotes + +async def cmd_emote_help(): + normal = ['emote', 'help'] + response = ( + 'Usage: emote reload\n' + 'Commands:\n' + ' emote reload......try to reload the emote schema (existing messages are not modified)\n' + ) + return normal, response + +async def cmd_emote_reload(): + try: + emotes = await load_emote_schema_async(CONFIG['EMOTE_SCHEMA']) + except OSError as e: + raise CommandFailed(f'could not read emote schema: {e}') from e + except json.JSONDecodeError as e: + raise CommandFailed('could not decode emote schema as json') from e + except BadEmote as e: + error, *_ = e.args + raise CommandFailed(error) from e + else: + # Mutate current_app.emotes in place + EMOTES.clear() + for emote in emotes: + EMOTES.append(emote) + # Clear emote markup cache -- emotes by the same name may have changed + get_emote_markup.cache_clear() + normal = ['emote', 'reload'] + response = '' + return normal, response + +SPEC = Str({ + None: End(cmd_emote_help), + 'help': End(cmd_emote_help), + 'reload': End(cmd_emote_reload), +}) + diff --git a/anonstream/control/spec/methods/help.py b/anonstream/control/spec/methods/help.py index ab0e5c7..2cd7d2f 100644 --- a/anonstream/control/spec/methods/help.py +++ b/anonstream/control/spec/methods/help.py @@ -24,6 +24,7 @@ async def cmd_help(): ' 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' + ' emote reload...................try reloading the emote schema\n' ) return normal, response diff --git a/anonstream/emote.py b/anonstream/emote.py index 26bc5dc..ab7bb1d 100644 --- a/anonstream/emote.py +++ b/anonstream/emote.py @@ -1,6 +1,7 @@ import json import re +import aiofiles from quart import escape class BadEmote(Exception): @@ -9,15 +10,23 @@ class BadEmote(Exception): class BadEmoteName(BadEmote): pass +def _load_emote_schema(emotes): + for key in ('name', 'file', 'width', 'height'): + for emote in emotes: + if key not in emote: + raise BadEmote(f'emotes must have a `{key}`: {emote}') + precompute_emote_regex(emotes) + return emotes + def load_emote_schema(filepath): with open(filepath) as fp: emotes = json.load(fp) - for key in ('name', 'file', 'width', 'height'): - for emote in emotes: - if key not in emote: - raise BadEmote(f'emotes must have a `{key}`: {emote}') - precompute_emote_regex(emotes) - return emotes + return _load_emote_schema(emotes) + +async def load_emote_schema_async(filepath): + async with aiofiles.open(filepath) as fp: + data = await fp.read(8192) + return _load_emote_schema(json.loads(data)) def precompute_emote_regex(schema): for emote in schema: