diff options
author | lovetox <philipp@hoerist.com> | 2022-08-12 16:15:55 +0300 |
---|---|---|
committer | lovetox <philipp@hoerist.com> | 2022-08-15 01:27:06 +0300 |
commit | a8fa42d2c37823f771ef38fa72e8b210f0f37e4c (patch) | |
tree | b907d172c213a86f86f63f266b64e0cc1da00624 | |
parent | b80315b8c7b8417043f1e69757610f316e4c1170 (diff) |
Initial commitnew-chat-commands
-rw-r--r-- | gajim/common/app.py | 2 | ||||
-rw-r--r-- | gajim/common/application.py | 2 | ||||
-rw-r--r-- | gajim/common/commands.py | 131 | ||||
-rw-r--r-- | gajim/common/modules/contacts.py | 24 | ||||
-rw-r--r-- | gajim/gtk/chat_action_processor.py | 18 | ||||
-rw-r--r-- | gajim/gtk/chat_stack.py | 3 | ||||
-rw-r--r-- | gajim/gtk/message_actions_box.py | 11 | ||||
-rw-r--r-- | pyrightconfig.json | 1 |
8 files changed, 176 insertions, 16 deletions
diff --git a/gajim/common/app.py b/gajim/common/app.py index 9d0544d34..964fd8539 100644 --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -63,6 +63,7 @@ if typing.TYPE_CHECKING: from gajim.common.call_manager import CallManager from gajim.common.preview import PreviewManager from gajim.common.task_manager import TaskManager + from gajim.common.commands import ChatCommands interface = cast(types.InterfaceT, None) @@ -77,6 +78,7 @@ bob_cache: dict[str, bytes] = {} ipython_window = None app = None # type: GajimApplication window = None # type: MainWindow +commands = None # type: ChatCommands ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher plugin_manager = cast(types.PluginManagerT, None) # Plugins Manager diff --git a/gajim/common/application.py b/gajim/common/application.py index 2a2930946..5817d9eb1 100644 --- a/gajim/common/application.py +++ b/gajim/common/application.py @@ -37,6 +37,7 @@ from gajim.common import ged from gajim.common import configpaths from gajim.common import logging_helpers from gajim.common import passwords +from gajim.common.commands import ChatCommands from gajim.common.dbus import logind from gajim.common.events import AccountDisonnected from gajim.common.events import AllowGajimUpdateCheck @@ -68,6 +69,7 @@ class CoreApplication: app.settings.init() app.config = LegacyConfig() + app.commands = ChatCommands() app.storage.cache = CacheStorage() app.storage.cache.init() diff --git a/gajim/common/commands.py b/gajim/common/commands.py new file mode 100644 index 000000000..6d97e8903 --- /dev/null +++ b/gajim/common/commands.py @@ -0,0 +1,131 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <http://www.gnu.org/licenses/>. + +from typing import Any, Callable + +import io +import argparse +import shlex + +from gajim.common import app +from gajim.common.helpers import Observable + + +def split_argument_string(string: str) -> list[str]: + ''' + Split a string with shlex.split + ''' + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = '' + result: list[str] = [] + + try: + for token in lex: + result.append(token) + except ValueError: + # If end-of-string is reached and there is a invalid state + # ValueError is raised. Still add the partial token to the result. + result.append(lex.token) + + return result + + +def get_usage_from_command(cmd: argparse.ArgumentParser) -> str: + with io.StringIO() as output: + cmd.print_usage(file=output) + usage = output.getvalue() + + usage = usage.split('[-h] ')[1] + return usage.strip() + + +class ArgumentParserError(Exception): + pass + + +class ArgumentParser(argparse.ArgumentParser): + def error(self, message): + raise ArgumentParserError(message) + + +class ChatCommands(Observable): + def __init__(self) -> None: + Observable.__init__(self) + self._parser = ArgumentParser(prog='ChatCommands') + self._sub_parser = self._parser.add_subparsers() + self._commands: dict[str, tuple[list[str], str]] = {} + + self._create_commands() + + def get_commands(self, used_in: str) -> list[tuple[str, str]]: + commands: list[tuple[str, str]] = [] + for cmd_name, cmd in self._commands.items(): + if used_in in cmd[0]: + commands.append((cmd_name, cmd[1])) + return commands + + def make_parser(self, + command_name: str, + callback: Callable[..., Any], + **kwargs: Any) -> ArgumentParser: + + '''Add and return a subparser and initialize + it with the command name. + ''' + + parser = self._sub_parser.add_parser(command_name, **kwargs) + parser.set_defaults(command_name=command_name) + self.connect(command_name, callback) + return parser + + def add_command(self, + command_name: str, + used_in: list[str], + cmd: argparse.ArgumentParser + ) -> None: + + usage = get_usage_from_command(cmd) + self._commands[command_name] = (used_in, usage) + + def parse(self, type_: str, arg_string: str) -> None: + arg_list = split_argument_string(arg_string[1:]) + args = self._parser.parse_args(arg_list) + + command = self._commands.get(args.command_name) + if command is None or type_ not in command[0]: + raise ArgumentParserError('Unknown command') + + self.notify(args.command_name, args) + + def _create_commands(self) -> None: + parser = self.make_parser('away', self._away_command) + parser.add_argument('message', default=None, nargs='?') + self.add_command('away', ['chat'], parser) + + def _away_command(self, + chat_commands: Any, + signal_name: str, + args: Any) -> None: + + for client in app.get_clients(): + if not app.settings.get_account_setting(client.account, + 'sync_with_global_status'): + continue + + if not client.state.is_available: + continue + + client.change_status('away', args.message or client.status_message) diff --git a/gajim/common/modules/contacts.py b/gajim/common/modules/contacts.py index 2f20cd2d5..e09696228 100644 --- a/gajim/common/modules/contacts.py +++ b/gajim/common/modules/contacts.py @@ -309,6 +309,10 @@ class CommonContact(Observable): return disco_info.supports(requested_feature) @property + def is_chat(self) -> bool: + return False + + @property def is_groupchat(self) -> bool: return False @@ -317,6 +321,10 @@ class CommonContact(Observable): return False @property + def type_string(self) -> str: + raise NotImplementedError + + @property def is_jingle_available(self) -> bool: return False @@ -558,6 +566,14 @@ class BareContact(CommonContact): return False return self.is_available + @property + def is_chat(self) -> bool: + return True + + @property + def type_string(self) -> str: + return 'chat' + class ResourceContact(CommonContact): def __init__(self, logger: LogAdapter, jid: JID, account: str) -> None: @@ -791,6 +807,10 @@ class GroupchatContact(CommonContact): room = self.settings.get('notify_on_all_messages') return all_ or room + @property + def type_string(self) -> str: + return 'groupchat' + class GroupchatParticipant(CommonContact): def __init__(self, logger: LogAdapter, jid: JID, account: str) -> None: @@ -936,6 +956,10 @@ class GroupchatParticipant(CommonContact): app.app.avatar_storage.invalidate_cache(self._jid) self.notify('user-avatar-update') + @property + def type_string(self) -> str: + return 'pm' + def can_add_to_roster(contact: Union[BareContact, GroupchatContact, diff --git a/gajim/gtk/chat_action_processor.py b/gajim/gtk/chat_action_processor.py index 749f9e3cc..9ad1c78d5 100644 --- a/gajim/gtk/chat_action_processor.py +++ b/gajim/gtk/chat_action_processor.py @@ -121,17 +121,9 @@ class ChatActionProcessor(Gtk.Popover): self._buf.delete(start_iter, self._current_iter) self._buf.insert(start_iter, selected_action) - def _get_commands(self) -> list[str]: - # TODO - # commands: list[str] = [] - # assert self._account - # assert self._contact - # control = app.window.get_control() - # for command in control.list_commands(): - # for name in command.names: - # commands.append(name) - # return commands - return [] + def _get_commands(self) -> list[tuple[str, str]]: + assert self._contact is not None + return app.commands.get_commands(self._contact.type_string) def _on_changed(self, _textview: MessageInputTextView) -> None: insert = self._buf.get_insert() @@ -181,7 +173,7 @@ class ChatActionProcessor(Gtk.Popover): self._menu.remove_all() command_list = self._get_commands() num_entries = 0 - for command in command_list: + for command, usage in command_list: if not command.startswith(action_text[1:]): continue if num_entries >= MAX_ENTRIES: @@ -189,7 +181,7 @@ class ChatActionProcessor(Gtk.Popover): action_data = GLib.Variant('s', f'/{command}') menu_item = Gio.MenuItem() - menu_item.set_label(f'/{command}') + menu_item.set_label(f'/{command} {usage}') menu_item.set_attribute_value('action-data', action_data) self._menu.append_item(menu_item) num_entries += 1 diff --git a/gajim/gtk/chat_stack.py b/gajim/gtk/chat_stack.py index 37cb2e7f1..456d56827 100644 --- a/gajim/gtk/chat_stack.py +++ b/gajim/gtk/chat_stack.py @@ -683,9 +683,6 @@ class ChatStack(Gtk.Stack, EventHelper): if message in ('', None, '\n'): return - # if process_commands and self.process_as_command(message): - # return - label = self._message_action_box.get_seclabel() correct_id = None diff --git a/gajim/gtk/message_actions_box.py b/gajim/gtk/message_actions_box.py index a0e3686b3..077ac81f2 100644 --- a/gajim/gtk/message_actions_box.py +++ b/gajim/gtk/message_actions_box.py @@ -38,6 +38,7 @@ from gajim.common import app from gajim.common import events from gajim.common import i18n from gajim.common import ged +from gajim.common.commands import ArgumentParserError from gajim.common.i18n import _ from gajim.common.client import Client from gajim.common.const import SimpleClientState @@ -594,6 +595,16 @@ class MessageActionsBox(Gtk.Grid, ged.EventHelper): textview.insert_newline() return True + message = self.msg_textview.get_text() + if message.startswith('/'): + try: + app.commands.parse(self._contact.type_string, message) + except ArgumentParserError as error: + print(error) + else: + self.msg_textview.clear() + return True + assert self._contact is not None if not app.account_is_available(self._contact.account): # we are not connected diff --git a/pyrightconfig.json b/pyrightconfig.json index 4cac3c1cc..3b2fb60fb 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -22,6 +22,7 @@ "gajim/common/application.py", "gajim/common/call_manager.py", "gajim/common/cert_store.py", + "gajim/common/commands.py", "gajim/common/configpaths.py", "gajim/common/const.py", "gajim/common/events.py", |