From 225d00231e817cc937ea95eb162dea0c52a8298f Mon Sep 17 00:00:00 2001 From: Daniel Reed Date: Thu, 4 Jul 2019 16:08:50 -0700 Subject: [PATCH] WIP: Have metabot.modules.admin.default use metabot.util.adminui.fields instead of custom code to select a module. This changes the signature of all MODULE.admin functions to contain the parent's conf object (so modconf becomes botconf), the current frame's field's name in that conf (modconf = botconf[field]), and the current frame's description (currently unused). Followups will likely continue this to have admin.default use adminui.fields even earlier (for bot selection), as well as places like moderator.admin (for group selection), but doing so would currently require the functions to be split into multiple callables. Thought should be given as to whether to go ahead and break adminui.fields into a callable adminui.Menu (as sort of a partial function). This also requires field descriptions to be calculated every time the fields are processed, even if the descriptions are never used. Thought should be given as to whether to actually separate the field name and type list from the description list, or to maybe do something like: ('fieldname', adminui.fieldtype, lambda: 'field description'), i.e. make the desc a callable. There's a similar concern about a theoretical invalid_msg and empty_msg standardization for adminui.fields (or adminui.Menu): Right now admin.default actually opens a file on disk every time it displays the theoretical invalid_msg. If this becomes a string passed to adminui.fields/Menu, the file will be read every time the user navigates anywhere within /admin (not just at the top-level bot selection menu). See #63. --- metabot/modules/admin.py | 33 +++++++++++-------------------- metabot/modules/countdown.py | 3 ++- metabot/modules/echo.py | 3 ++- metabot/modules/groups.py | 3 ++- metabot/modules/help.py | 3 ++- metabot/modules/moderator.py | 13 ++++++++++-- metabot/modules/telegram.py | 3 ++- metabot/modules/test_admin.py | 2 +- metabot/modules/test_moderator.py | 4 ++-- metabot/util/adminui.py | 4 ++-- 10 files changed, 37 insertions(+), 34 deletions(-) diff --git a/metabot/modules/admin.py b/metabot/modules/admin.py index 6a770ab..62a1cfe 100644 --- a/metabot/modules/admin.py +++ b/metabot/modules/admin.py @@ -2,6 +2,8 @@ import uuid +from metabot.util import adminui + BOOTSTRAP_TOKEN = uuid.uuid4().hex @@ -37,7 +39,7 @@ def default(ctx, msg): # pylint: disable=missing-docstring msg.path('/admin', 'Bot Admin') - username, modname, text = ctx.split(3) + username, text = ctx.split(2) if username not in bots: msg.action = 'Choose a bot' @@ -53,33 +55,19 @@ def default(ctx, msg): # pylint: disable=missing-docstring return msg.path(username) + ctx.targetbotuser = username + ctx.targetbotconf = ctx.bot.multibot.conf['bots'][username] - modules = { - modname: module - for modname, module in ctx.bot.multibot.modules.items() - if hasattr(module, 'admin') - } + modules = sorted((modname, module.admin, module.__doc__.splitlines()[0].rstrip('.')) + for modname, module in ctx.bot.multibot.modules.items() + if hasattr(module, 'admin')) if not modules: # pragma: no cover return msg.add( "Hi! There aren't any configurable modules installed. Contact a metabot admin to " 'install one.') - if modname not in modules: - msg.action = 'Choose a module' - for modname, module in sorted(modules.items()): - label = modname - if getattr(module, '__doc__', None): - label = '%s \u2022 %s' % (label, module.__doc__.splitlines()[0].rstrip('.')) - msg.button(label, '/admin %s %s' % (username, modname)) - return - - msg.path(modname) - - admin_callback = modules[modname].admin - ctx.targetbotuser = username - ctx.targetbotconf = ctx.bot.multibot.conf['bots'][username] - return admin_callback(ctx, msg, ctx.targetbotconf['issue37'][modname], text) + return adminui.fields(ctx, msg, ctx.targetbotconf['issue37'], modules, text, what='module') def bootstrap(ctx, msg, modconf): @@ -90,9 +78,10 @@ def bootstrap(ctx, msg, modconf): msg.add('Added %s to the admin list.', ctx.user['id']) -def admin(ctx, msg, modconf, text): # pylint: disable=too-many-branches +def admin(ctx, msg, botconf, field, unused_desc, text): # pylint: disable=too-many-branches """Handle /admin BOTNAME admin (configure the admin module itself).""" + modconf = botconf[field] if 'admins' not in modconf: # pragma: no cover modconf['admins'] = [] diff --git a/metabot/modules/countdown.py b/metabot/modules/countdown.py index 4edaf85..f3d2998 100644 --- a/metabot/modules/countdown.py +++ b/metabot/modules/countdown.py @@ -52,9 +52,10 @@ def format_delta(seconds): return 'NOW' -def admin(unused_ctx, msg, modconf, text): +def admin(unused_ctx, msg, botconf, field, unused_desc, text): """Handle /admin BOTNAME countdown.""" + modconf = botconf[field] command, _, timestamp = text.partition(' ') command = command.lower() diff --git a/metabot/modules/echo.py b/metabot/modules/echo.py index d4903ef..f2647e8 100644 --- a/metabot/modules/echo.py +++ b/metabot/modules/echo.py @@ -34,9 +34,10 @@ def echo(ctx, msg, data): # pylint: disable=missing-docstring msg.button('More (%i/%i)' % (page, len(lines)), '/%s %i' % (ctx.command, page + 1)) -def admin(ctx, msg, modconf, text): +def admin(ctx, msg, botconf, field, unused_desc, text): """Handle /admin BOTNAME echo.""" + modconf = botconf[field] command, _, text = text.partition(' ') command = command.lower() diff --git a/metabot/modules/groups.py b/metabot/modules/groups.py index a1c6ab9..29d1cb1 100644 --- a/metabot/modules/groups.py +++ b/metabot/modules/groups.py @@ -100,9 +100,10 @@ def inline(ctx, modconf): ctx.reply_inline(results, cache_time=60) -def admin(ctx, msg, modconf, text): +def admin(ctx, msg, botconf, field, unused_desc, text): """Handle /admin BOTNAME groups.""" + modconf = botconf[field] if text.startswith('remove '): text = text[len('remove '):] group = modconf['groups'].pop(text) diff --git a/metabot/modules/help.py b/metabot/modules/help.py index f66e7e8..07c6720 100644 --- a/metabot/modules/help.py +++ b/metabot/modules/help.py @@ -34,9 +34,10 @@ def default(ctx, msg, modconf): # pylint: disable=missing-docstring msg.add('%s', line) -def admin(ctx, msg, modconf, text): # pylint: disable=too-many-branches +def admin(ctx, msg, botconf, field, unused_desc, text): # pylint: disable=too-many-branches """Handle /admin BOTNAME help.""" + modconf = botconf[field] action, _, modname = text.partition(' ') hidden = set(modconf.get('hidden', '').split()) diff --git a/metabot/modules/moderator.py b/metabot/modules/moderator.py index 92c50aa..e2040f8 100644 --- a/metabot/modules/moderator.py +++ b/metabot/modules/moderator.py @@ -65,7 +65,13 @@ def moddispatch(ctx, msg, modconf): # pylint: disable=missing-docstring ctx.private = True msg.path('/mod', 'Group Admin') ctx.targetbotconf = ctx.bot.config - return admin(ctx, msg, modconf, ctx.text, botadmin=False) + return admin(ctx, + msg, + ctx.bot.config['issue37'], + 'moderator', + 'Group Admin', + ctx.text, + botadmin=False) return False @@ -105,17 +111,20 @@ def join(ctx, msg, modconf): msg.add(greeting) -def admin(ctx, msg, modconf, text, botadmin=True): +def admin(ctx, msg, botconf, field, unused_desc, text, botadmin=True): # pylint: disable=too-many-arguments """Handle /admin BOTNAME moderator.""" + modconf = botconf[field] groups = sorted( group_id for group_id in modconf if botadmin or ctx.user['id'] in ctx.bot.multibot.conf['groups'][int(group_id)]['admins']) if not groups: if botadmin: + msg.action = 'Add me to a group' return msg.add( "I'm not in any groups! Add me to an existing group from its details screen.") + msg.action = 'Become a group admin' return msg.add( "Hi! You aren't an admin in any groups I'm in. If you should be, ask a current admin " "to promote you from the group's members list.") diff --git a/metabot/modules/telegram.py b/metabot/modules/telegram.py index 7b87324..8bbfeb1 100644 --- a/metabot/modules/telegram.py +++ b/metabot/modules/telegram.py @@ -1,9 +1,10 @@ """Manage the bot's Telegram state.""" -def admin(ctx, msg, modconf, text): +def admin(ctx, msg, botconf, field, unused_desc, text): """Handle /admin BOTNAME telegram (manage the bot's Telegram state).""" + modconf = botconf[field] username = ctx.targetbotuser msg.action = 'Choose an action' diff --git a/metabot/modules/test_admin.py b/metabot/modules/test_admin.py index be23aa6..d20db22 100644 --- a/metabot/modules/test_admin.py +++ b/metabot/modules/test_admin.py @@ -47,7 +47,7 @@ def test_default(conversation): # pylint: disable=redefined-outer-name assert conversation.message('/admin modulestestbot') == """\ [chat_id=1000 disable_web_page_preview=True parse_mode=HTML] Bot Admin › modulestestbot: Choose a module -[admin • Manage the admin list | /admin modulestestbot admin] +[admin (…) • Manage the admin list | /admin modulestestbot admin] [help • Return the list of commands and other bot features | /admin modulestestbot help] [Back | /admin] """ diff --git a/metabot/modules/test_moderator.py b/metabot/modules/test_moderator.py index 8ae940b..21cd1bb 100644 --- a/metabot/modules/test_moderator.py +++ b/metabot/modules/test_moderator.py @@ -35,7 +35,7 @@ def test_mod(conversation): # pylint: disable=redefined-outer-name assert conversation.message('/mod', user_id=2000) == """\ [chat_id=2000 disable_web_page_preview=True parse_mode=HTML] -Group Admin +Group Admin: Become a group admin Hi! You aren't an admin in any groups I'm in. If you should be, ask a current admin to promote you from the group's members list. """ @@ -73,7 +73,7 @@ def test_admin(conversation): # pylint: disable=redefined-outer-name assert conversation.message('/admin modulestestbot moderator') == """\ [chat_id=1000 disable_web_page_preview=True parse_mode=HTML] -Bot Admin › modulestestbot › moderator +Bot Admin › modulestestbot › moderator: Add me to a group I'm not in any groups! Add me to an existing group from its details screen. [Back | /admin modulestestbot] diff --git a/metabot/util/adminui.py b/metabot/util/adminui.py index 2bbf62a..3dfbadb 100644 --- a/metabot/util/adminui.py +++ b/metabot/util/adminui.py @@ -123,7 +123,7 @@ def daysofweek(unused_ctx, msg, subconf, field, desc, text): # pylint: disable= msg.buttons(buttons) -def fields(ctx, msg, subconf, fieldset, text): # pylint: disable=too-many-arguments +def fields(ctx, msg, subconf, fieldset, text, what='field'): # pylint: disable=too-many-arguments """Present a menu of fields to edit.""" field, _, text = text.partition(' ') @@ -139,7 +139,7 @@ def fields(ctx, msg, subconf, fieldset, text): # pylint: disable=too-many-argum msg.add("I can't set %s.", field) if not msg.action: - msg.action = 'Choose a field' + msg.action = 'Choose a ' + what for fieldname, uifunc, fielddesc in fieldset: value = subconf.get(fieldname) if uifunc is bool: