Queer European MD passionate about IT
瀏覽代碼

Finished working on /father command

Davte 5 年之前
父節點
當前提交
ca6dca3cb7
共有 2 個文件被更改,包括 518 次插入56 次删除
  1. 323 28
      davtelepot/administration_tools.py
  2. 195 28
      davtelepot/messages.py

+ 323 - 28
davtelepot/administration_tools.py

@@ -13,6 +13,7 @@ import asyncio
 import datetime
 import json
 import logging
+import re
 import types
 
 from collections import OrderedDict
@@ -34,6 +35,8 @@ from .utilities import (
 # Use this parameter in SQL `LIMIT x OFFSET y` clauses
 rows_number_limit = 10
 
+command_description_parser = re.compile(r'(?P<command>\w+)(\s?-\s?(?P<description>.*))?')
+
 
 async def _forward_to(update,
                       bot: Bot,
@@ -1059,7 +1062,7 @@ def get_custom_commands(bot: Bot, language: str = None) -> List[dict]:
             hidden=False
         )
     ]
-    hidden_commands = [
+    hidden_commands_names = [
         record['command']
         for record in bot.db['bot_father_commands'].find(
             cancelled=None,
@@ -1069,9 +1072,10 @@ def get_custom_commands(bot: Bot, language: str = None) -> List[dict]:
     return sorted(
         [
             command
-            for command in get_current_commands(bot=bot, language=language)
-            if command['command'] not in hidden_commands
-        ] + additional_commands,
+            for command in (get_current_commands(bot=bot, language=language)
+                            + additional_commands)
+            if command['command'] not in hidden_commands_names
+        ],
         key=(lambda c: c['command'])
     )
 
@@ -1144,7 +1148,11 @@ def browse_bot_father_settings_records(bot: Bot,
         language=language,
         record_interval=((page * rows_number_limit + 1) if records else 0,
                          min((page + 1) * rows_number_limit, len(records)),
-                         records_count)
+                         records_count),
+        commands_list='\n'.join(
+            f"{'➖' if record['hidden'] else '➕'} {record['command']}"
+            for record in records[:rows_number_limit]
+        )
     )
     buttons = make_lines_of_buttons(
         [
@@ -1154,7 +1162,7 @@ def browse_bot_father_settings_records(bot: Bot,
                 delimiter='|',
                 data=['settings', 'edit', 'select', record['id']]
             )
-            for record in records
+            for record in records[:rows_number_limit]
         ],
         3
     )
@@ -1198,10 +1206,144 @@ def browse_bot_father_settings_records(bot: Bot,
     return result, text, reply_markup
 
 
+def get_bot_father_settings_editor(mode: str,
+                                   record: OrderedDict = None):
+    """Get a coroutine to edit or create a record in bot father settings table.
+
+    Modes:
+        - add
+        - hide
+    """
+    async def bot_father_settings_editor(bot: Bot, update: dict,
+                                         language: str):
+        """Edit or create a record in bot father settings table."""
+        nonlocal record
+        if record is not None:
+            record_id = record['id']
+        else:
+            record_id = None
+        # Cancel if user used /cancel command, or remove trailing forward_slash
+        input_text = update['text']
+        if input_text.startswith('/'):
+            if language not in bot.messages['admin']['cancel']['lower']:
+                language = bot.default_language
+            if input_text.lower().endswith(bot.messages['admin']['cancel']['lower'][language]):
+                return bot.get_message(
+                    'admin', 'cancel', 'done',
+                    language=language
+                )
+            else:
+                input_text = input_text[1:]
+        if record is None:
+            # Use regex compiled pattern to search for command and description
+            re_search = command_description_parser.search(input_text)
+            if re_search is None:
+                return bot.get_message(
+                    'admin', 'error', 'text',
+                    language=language
+                )
+            re_search = re_search.groupdict()
+            command = re_search['command'].lower()
+            description = re_search['description']
+        else:
+            command = record['command']
+            description = input_text
+        error = None
+        # A description (str 3-256) is required
+        if mode in ('add', 'edit'):
+            if description is None or len(description) < 3:
+                error = 'missing_description'
+            elif type(description) is str and len(description) > 255:
+                error = 'description_too_long'
+            elif mode == 'add':
+                duplicate = bot.db['bot_father_commands'].find_one(
+                    command=command,
+                    cancelled=None
+                )
+                if duplicate:
+                    error = 'duplicate_record'
+        if error:
+            text = bot.get_message(
+                'admin', 'father_command', 'settings', 'modes',
+                'add', 'error', error,
+                language=language
+            )
+            reply_markup = make_inline_keyboard(
+                [
+                    make_button(
+                        text=bot.get_message(
+                            'admin', 'father_command', 'back',
+                            language=language
+                        ),
+                        prefix='father:///',
+                        delimiter='|',
+                        data=['settings']
+                    )
+                ]
+            )
+        else:
+            table = bot.db['bot_father_commands']
+            new_record = dict(
+                command=command,
+                description=description,
+                hidden=(mode == 'hide'),
+                cancelled=None
+            )
+            if record_id is None:
+                record_id = table.insert(
+                    new_record
+                )
+            else:
+                new_record['id'] = record_id
+                table.upsert(
+                    new_record,
+                    ['id']
+                )
+            text = bot.get_message(
+                'admin', 'father_command', 'settings', 'modes',
+                mode, ('edit' if 'id' in new_record else 'add'), 'done',
+                command=command,
+                description=(description if description else '-'),
+                language=language
+            )
+            reply_markup = make_inline_keyboard(
+                [
+                    make_button(
+                        text=bot.get_message(
+                            'admin', 'father_command', 'settings', 'modes',
+                            'edit', 'button',
+                            language=language
+                        ),
+                        prefix='father:///',
+                        delimiter='|',
+                        data=['settings', 'edit', 'select', record_id]
+                    ), make_button(
+                        text=bot.get_message(
+                            'admin', 'father_command', 'back',
+                            language=language
+                        ),
+                        prefix='father:///',
+                        delimiter='|',
+                        data=['settings']
+                    )
+                ],
+                2
+            )
+        asyncio.ensure_future(
+            bot.delete_message(update=update)
+        )
+        return dict(
+            text=text,
+            reply_markup=reply_markup
+        )
+    return bot_father_settings_editor
+
+
 async def edit_bot_father_settings_via_message(bot: Bot,
                                                user_record: OrderedDict,
                                                language: str,
-                                               mode: str):
+                                               mode: str,
+                                               record: OrderedDict = None):
     result, text, reply_markup = '', '', None
     modes = bot.messages['admin']['father_command']['settings']['modes']
     if mode not in modes:
@@ -1210,13 +1352,35 @@ async def edit_bot_father_settings_via_message(bot: Bot,
             language=language
         )
     else:
-        result = "🚧 Not implemented yet!"
-        # TODO: write messages, set individual text message parser to create records
-        # result = bot.get_message(
-        #     messages=modes[mode],
-        #     language=language
-        # )
-        # bot.set_individual_text_message_handler()
+        result = bot.get_message(
+            ('add' if record is None else 'edit'), 'popup',
+            messages=modes[mode],
+            language=language,
+            command=(record['command'] if record is not None else None)
+        )
+        text = bot.get_message(
+            ('add' if record is None else 'edit'), 'text',
+            messages=modes[mode],
+            language=language,
+            command=(record['command'] if record is not None else None)
+        )
+        reply_markup = make_inline_keyboard(
+            [
+                make_button(
+                    text=bot.get_message(
+                        'admin', 'cancel', 'button',
+                        language=language,
+                    ),
+                    prefix='father:///',
+                    delimiter='|',
+                    data=['cancel']
+                )
+            ]
+        )
+        bot.set_individual_text_message_handler(
+            get_bot_father_settings_editor(mode=mode, record=record),
+            user_id=user_record['telegram_id'],
+        )
     return result, text, reply_markup
 
 
@@ -1231,7 +1395,24 @@ async def _father_button(bot: Bot, user_record: OrderedDict,
     """
     result, text, reply_markup = '', '', None
     command, *data = data
-    if command == 'get':
+    if command == 'cancel':
+        bot.remove_individual_text_message_handler(user_id=user_record['telegram_id'])
+        result = text = bot.get_message(
+            'admin', 'cancel', 'done',
+            language=language
+        )
+        reply_markup = make_inline_keyboard(
+            [
+                make_button(
+                    text=bot.get_message('admin', 'father_command', 'back',
+                                         language=language),
+                    prefix='father:///',
+                    delimiter='|',
+                    data=['main']
+                )
+            ]
+        )
+    elif command == 'get':
         commands = await bot.getMyCommands()
         text = '<code>' + '\n'.join(
             "{c[command]} - {c[description]}".format(c=command)
@@ -1255,13 +1436,13 @@ async def _father_button(bot: Bot, user_record: OrderedDict,
         )
     elif command == 'set':
         stored_commands = await bot.getMyCommands()
-        current_commands = get_current_commands(bot=bot, language=language)
+        current_commands = get_custom_commands(bot=bot, language=language)
         if len(data) > 0 and data[0] == 'confirm':
             if not Confirmator.get('set_bot_father_commands',
                                    confirm_timedelta=3
                                    ).confirm(user_record['id']):
                 return bot.get_message(
-                    'admin', 'father_command', 'confirm',
+                    'admin', 'confirm',
                     language=language
                 )
             if stored_commands == current_commands:
@@ -1270,13 +1451,19 @@ async def _father_button(bot: Bot, user_record: OrderedDict,
                     language=language
                 )
             else:
-                await bot.setMyCommands(
-                    current_commands
-                )
-                text = bot.get_message(
-                    'admin', 'father_command', 'set', 'done',
-                    language=language
-                )
+                if isinstance(
+                        await bot.setMyCommands(current_commands),
+                        Exception
+                ):
+                    text = bot.get_message(
+                        'admin', 'father_command', 'set', 'error',
+                        language=language
+                    )
+                else:
+                    text = bot.get_message(
+                        'admin', 'father_command', 'set', 'done',
+                        language=language
+                    )
             reply_markup = make_inline_keyboard(
                 [
                     make_button(
@@ -1289,17 +1476,24 @@ async def _father_button(bot: Bot, user_record: OrderedDict,
                 ]
             )
         else:
+            stored_commands_names = [c['command'] for c in stored_commands]
+            current_commands_names = [c['command'] for c in current_commands]
+            # Show preview of new, edited and removed commands
+            # See 'legend' in bot.messages['admin']['father_command']['set']
             text = bot.get_message(
                     'admin', 'father_command', 'set', 'header',
                     language=language
             ) + '\n\n' + '\n\n'.join([
                 '\n'.join(
-                    ('✅ ' if c in stored_commands else '☑️ ') + c['command']
+                    ('✅ ' if c in stored_commands
+                     else '☑️ ' if c['command'] not in stored_commands_names
+                     else '✏️') + c['command']
                     for c in current_commands
                 ),
                 '\n'.join(
-                    f'❌ {c["command"]}'
-                    for c in stored_commands if c not in current_commands
+                    f'❌ {command}'
+                    for command in stored_commands_names
+                    if command not in current_commands_names
                 ),
                 bot.get_message(
                     'admin', 'father_command', 'set', 'legend',
@@ -1387,7 +1581,108 @@ async def _father_button(bot: Bot, user_record: OrderedDict,
         elif data[0] == 'edit':
             if len(data) > 2 and data[1] == 'select':
                 selected_record = bot.db['bot_father_commands'].find_one(id=data[2])
-                # TODO: show selected record and actions on it
+                if selected_record is None:
+                    return bot.get_message(
+                        'admin', 'error',
+                        language=language
+                    )
+                if len(data) == 3:
+                    text = bot.get_message(
+                        'admin', 'father_command', 'settings',
+                        'modes', 'edit', 'panel', 'text',
+                        language=language,
+                        command=selected_record['command'],
+                        description=selected_record['description'],
+                    )
+                    reply_markup = make_inline_keyboard(
+                        [
+                            make_button(
+                                text=bot.get_message(
+                                    'admin', 'father_command', 'settings',
+                                    'modes', 'edit', 'panel',
+                                    'edit_description', 'button',
+                                    language=language,
+                                ),
+                                prefix='father:///',
+                                delimiter='|',
+                                data=['settings', 'edit', 'select',
+                                      selected_record['id'], 'edit_descr']
+                            ),
+                            make_button(
+                                text=bot.get_message(
+                                    'admin', 'father_command', 'settings',
+                                    'modes', 'edit', 'panel',
+                                    'delete', 'button',
+                                    language=language,
+                                ),
+                                prefix='father:///',
+                                delimiter='|',
+                                data=['settings', 'edit', 'select',
+                                      selected_record['id'], 'del']
+                            ),
+                            make_button(
+                                text=bot.get_message(
+                                    'admin', 'father_command', 'back',
+                                    language=language,
+                                ),
+                                prefix='father:///',
+                                delimiter='|',
+                                data=['settings', 'edit']
+                            )
+                        ],
+                        2
+                    )
+                elif len(data) > 3 and data[3] == 'edit_descr':
+                    result, text, reply_markup = await edit_bot_father_settings_via_message(
+                        bot=bot,
+                        user_record=user_record,
+                        language=language,
+                        mode=data[0],
+                        record=selected_record
+                    )
+                elif len(data) > 3 and data[3] == 'del':
+                    if not Confirmator.get('set_bot_father_commands',
+                                           confirm_timedelta=3
+                                           ).confirm(user_record['id']):
+                        result = bot.get_message(
+                            'admin', 'confirm',
+                            language=language
+                        )
+                    else:
+                        bot.db['bot_father_commands'].update(
+                            dict(
+                                id=selected_record['id'],
+                                cancelled=True
+                            ),
+                            ['id']
+                        )
+                        result = bot.get_message(
+                            'admin', 'father_command', 'settings',
+                            'modes', 'edit', 'panel', 'delete',
+                            'done', 'popup',
+                            language=language
+                        )
+                        text = bot.get_message(
+                            'admin', 'father_command', 'settings',
+                            'modes', 'edit', 'panel', 'delete',
+                            'done', 'text',
+                            language=language
+                        )
+                        reply_markup = make_inline_keyboard(
+                            [
+                                make_button(
+                                    text=bot.get_message(
+                                        'admin', 'father_command',
+                                        'back',
+                                        language=language
+                                    ),
+                                    prefix='father:///',
+                                    delimiter='|',
+                                    data=['settings']
+                                )
+                            ],
+                            1
+                        )
             elif len(data) == 1 or data[1] == 'go':
                 result, text, reply_markup = browse_bot_father_settings_records(
                     bot=bot,

+ 195 - 28
davtelepot/messages.py

@@ -1,6 +1,24 @@
 """Default messages for bot functions."""
 
 default_admin_messages = {
+    'cancel': {
+        'button': {
+            'en': "↩️ Cancel",
+            'it': "↩️ Annulla"
+        },
+        'done': {
+            'en': "↩️ Operation cancelled",
+            'it': "↩️ Operazione annullata",
+        },
+        'lower': {
+            'en': "cancel",
+            'it': "annulla",
+        },
+    },
+    'confirm': {
+        'en': "🔄 Click again to confirm",
+        'it': "🔄 Clicka di nuovo per confermare",
+    },
     'db_command': {
         'description': {
             'en': "Ask for bot database via Telegram",
@@ -23,6 +41,12 @@ default_admin_messages = {
             'it': "Database inviato."
         }
     },
+    'error': {
+        'text': {
+            'en': "❌️ Error!",
+            'it': "❌️ Errore!"
+        },
+    },
     'errors_command': {
         'description': {
             'en': "Receive bot error log file, if set",
@@ -71,10 +95,6 @@ default_admin_messages = {
             'en': "↩️ Back",
             'it': "↩️ Indietro",
         },
-        'confirm': {
-            'en': "🔄 Click again to confirm",
-            'it': "🔄 Clicka di nuovo per confermare",
-        },
         'description': {
             'en': "Edit the @BotFather commands",
             'it': "Modifica i comandi con @BotFather",
@@ -130,6 +150,10 @@ default_admin_messages = {
                 'en': "✅ Done!",
                 'it': "✅ Fatto!",
             },
+            'error': {
+                'en': "Something went wrong 😕",
+                'it': "Qualcosa è andato storto 😕",
+            },
             'header': {
                 'en': "✏️ <b>Change commands stored by @BotFather 🤖</b>",
                 'it': "✏️ <b>Modifica i comandi salvati su @BotFather 🤖</b>",
@@ -137,12 +161,14 @@ default_admin_messages = {
             'legend': {
                 'en': "<b>Legend</b>\n"
                       "✅ <i>Already stored</i>\n"
+                      "✏️ <i>New description</i>\n"
                       "☑ <i>New command</i>\n"
                       "❌ <i>Will be removed</i>",
                 'it': "<b>Legenda</b>\n"
                       "✅ <i>Già presente</i>\n"
+                      "✏️ <i>Nuova descrizione</i>\n"
                       "☑ <i>Nuovo comando</i>\n"
-                      "❌ <i>Comando eliminato</i>",
+                      "❌ <i>Comando da eliminare</i>",
             },
             'no_change': {
                 'en': "❌ No change detected",
@@ -152,53 +178,194 @@ default_admin_messages = {
         'settings': {
             'browse_records': {
                 'en': "✏️ <b>Edit BotFather settings</b> ⚙️\n\n"
-                      "Select a record to edit.\n"
-                      "Showing records from {record_interval[0]} to "
-                      "{record_interval[1]} of {record_interval[2]}\n\n"
+                      "Select a record to edit.\n\n"
+                      "{commands_list}\n\n"
                       "<i>Legend</i>\n"
                       "➕ Added commands\n"
-                      "➖ Hidden commands\n",
+                      "➖ Hidden commands\n\n"
+                      "Showing records from {record_interval[0]} to "
+                      "{record_interval[1]} of {record_interval[2]}",
                 'it': "✏️ <b>Modifica impostazioni di BotFather</b> ⚙\n\n️"
-                      "Seleziona un'impostazione da modificare.\n"
-                      "Record da {record_interval[0]} a "
-                      "{record_interval[1]} di {record_interval[2]}\n\n"
+                      "Seleziona un'impostazione da modificare.\n\n"
+                      "{commands_list}\n\n"
                       "<i>Legenda</i>\n"
                       "➕ Comandi aggiunti\n"
-                      "➖ Comandi nascosti\n",
+                      "➖ Comandi nascosti\n\n"
+                      "Record da {record_interval[0]} a "
+                      "{record_interval[1]} di {record_interval[2]}",
             },
             'modes': {
                 'add': {
-                    'symbol': "➕️",
-                    'name': {
-                        'en': "Add",
-                        'it': "Aggiungi"
+                    'add': {
+                        'done': {
+                            'en': "➕️️ <b>Added additional command</b>\n\n"
+                                  "Command: {command}\n"
+                                  "Description: {description}",
+                            'it': "➕️️ <b>Inserito comando aggiuntivo</b>\n\n"
+                                  "Comando: {command}\n"
+                                  "Descrizione: {description}",
+                        },
+                        'popup': {
+                            'en': "Write the command to add",
+                            'it': "Scrivimi il comando da aggiungere",
+                        },
+                        'text': {
+                            'en': "Write the command to add or /cancel this operation",
+                            'it': "Scrivimi il comando da aggiungere o /annulla",
+                        },
                     },
                     'description': {
                         'en': "Add command to default list",
                         'it': "Aggiungi un comando dalla lista autogenerata"
-                    }
+                    },
+                    'edit': {
+                        'done': {
+                            'en': "✏️ <b>Edited additional command</b>\n\n"
+                                  "Command: {command}\n"
+                                  "Description: {description}",
+                            'it': "✏️ <b>Comando da nascondere modificato</b>\n\n"
+                                  "Comando: {command}\n"
+                                  "Descrizione: {description}",
+                        },
+                    },
+                    'error': {
+                        'description_too_long': {
+                            'en': "<b>Description is too long</b>\n\n"
+                                  "Description length must be 3-256 chars.",
+                            'it': "<b>Descrizione troppo lunga</b>\n\n"
+                                  "La descrizione deve essere di 3-256 caratteri.",
+                        },
+                        'duplicate_record': {
+                            'en': "<b>Duplicate record</b>\n\n"
+                                  "Command is already being added to default "
+                                  "output. Edit that record if you need to.",
+                            'it': "<b>Record già presente</b>\n\n"
+                                  "Questo comando è già aggiunto a quelli di "
+                                  "default. Modifica il record già presente se "
+                                  "necessario.",
+                        },
+                        'missing_description': {
+                            'en': "<b>Missing description</b>\n\n"
+                                  "Additional commands must have a description "
+                                  "(3-256 chars).",
+                            'it': "<b>Descrizione mancante</b>\n\n"
+                                  "I comandi aggiuntivi devono avere una "
+                                  "descrizione di 3-256 caratteri.",
+                        },
+                        'unhandled_exception': {
+                            'en': "❌ <b>Unhandled exception </b> ⚠️",
+                            'it': "❌ <b>Errore imprevisto </b> ⚠️",
+                        },
+                    },
+                    'name': {
+                        'en': "Add",
+                        'it': "Aggiungi"
+                    },
+                    'symbol': "➕️",
                 },
                 'hide': {
-                    'symbol': "➖️",
-                    'name': {
-                        'en': "Hide",
-                        'it': "Nascondi"
+                    'add': {
+                        'done': {
+                            'en': "➖ <b>Added hidden command</b>\n\n"
+                                  "Command: {command}\n",
+                            'it': "➖ <b>Comando da nascondere aggiunto</b>"
+                                  "Comando: {command}\n",
+                        },
+                        'popup': {
+                            'en': "Write the command to hide",
+                            'it': "Scrivimi il comando da nascondere",
+                        },
+                        'text': {
+                            'en': "Write the command to hide or /cancel this operation",
+                            'it': "Scrivimi il comando da nascondere o /annulla",
+                        }
                     },
                     'description': {
                         'en': "Hide command from default list",
                         'it': "Nascondi un comando dalla lista autogenerata"
-                    }
+                    },
+                    'edit': {
+                        'done': {
+                            'en': "✏️ <b>Edited hidden command</b>\n\n"
+                                  "Command: {command}\n"
+                                  "Description: {description}",
+                            'it': "✏️ <b>Comando da nascondere modificato</b>\n\n"
+                                  "Comando: {command}\n"
+                                  "Descrizione: {description}",
+                        },
+                    },
+                    'name': {
+                        'en': "Hide",
+                        'it': "Nascondi"
+                    },
+                    'symbol': "➖️",
                 },
                 'edit': {
-                    'symbol': "✏️",
-                    'name': {
-                        'en': "Edit",
-                        'it': "Modifica"
+                    'button': {
+                        'en': "✏️ Edit record",
+                        'it': "✏️ Modifica record"
                     },
                     'description': {
                         'en': "Edit added or hidden commands",
                         'it': "Modifica i comandi aggiunti o nascosti"
-                    }
+                    },
+                    'edit': {
+                        'popup': {
+                            'en': "Write the new description",
+                            'it': "Scrivimi la nuova descrizione",
+                        },
+                        'text': {
+                            'en': "Write the new description for command "
+                                  "{command} or /cancel",
+                            'it': "Scrivimi la nuova descrizione per il  "
+                                  "comando {command} o /annulla",
+                        },
+                        'done': {
+                            'en': "✏️ Edit succeeded ✅\n\n"
+                                  "Command: {command}\n"""
+                                  "Description: {description}",
+                            'it': "✏️ Modifica completata ✅\n\n"
+                                  "Comando: {command}\n"""
+                                  "Descrizione: {description}",
+                        }
+                    },
+                    'name': {
+                        'en': "Edit",
+                        'it': "Modifica"
+                    },
+                    'panel': {
+                        'delete': {
+                            'button': {
+                                'en': "❌ Delete record",
+                                'it': "❌ Elimina record",
+                            },
+                            'done': {
+                                'popup': {
+                                    'en': "Record deleted ✅",
+                                    'it': "Record eliminato ✅",
+                                },
+                                'text': {
+                                    'en': "Record deleted ✅",
+                                    'it': "Record eliminato ✅",
+                                },
+                            },
+                        },
+                        'edit_description': {
+                            'button': {
+                                'en': "✏️ Edit description",
+                                'it': "✏️ Modifica descrizione",
+                            },
+                        },
+                        'text': {
+                            'en': "✏️ Edit record ✅\n\n"
+                                  "Command: {command}\n"""
+                                  "Description: {description}",
+                            'it': "✏️ Modifica record\n\n"
+                                  "Comando: {command}\n"""
+                                  "Descrizione: {description}",
+                        },
+                    },
+                    'symbol': "✏️",
                 },
             },
             'panel': {