diff options
Diffstat (limited to 'poezio/tabs/privatetab.py')
-rw-r--r-- | poezio/tabs/privatetab.py | 244 |
1 files changed, 157 insertions, 87 deletions
diff --git a/poezio/tabs/privatetab.py b/poezio/tabs/privatetab.py index 8d2c1b11..1909e3c1 100644 --- a/poezio/tabs/privatetab.py +++ b/poezio/tabs/privatetab.py @@ -10,22 +10,30 @@ both participant’s nicks. It also has slightly different features than the ConversationTab (such as tab-completion on nicks from the room). """ +import asyncio import curses import logging +from datetime import datetime from typing import Dict, Callable from slixmpp import JID +from slixmpp.stanza import Message as SMessage from poezio.tabs import OneToOneTab, MucTab, Tab +from poezio import common from poezio import windows from poezio import xhtml -from poezio.config import config +from poezio.config import config, get_image_cache from poezio.core.structs import Command from poezio.decorators import refresh_wrapper -from poezio.logger import logger from poezio.theming import get_theme, dump_tuple from poezio.decorators import command_args_parser +from poezio.text_buffer import CorrectionError +from poezio.ui.types import ( + Message, + PersistentInfoMessage, +) log = logging.getLogger(__name__) @@ -34,16 +42,14 @@ class PrivateTab(OneToOneTab): """ The tab containing a private conversation (someone from a MUC) """ - plugin_commands = {} # type: Dict[str, Command] - plugin_keys = {} # type: Dict[str, Callable] + plugin_commands: Dict[str, Command] = {} + plugin_keys: Dict[str, Callable] = {} message_type = 'chat' - additional_information = {} # type: Dict[str, Callable[[str], str]] + additional_information: Dict[str, Callable[[str], str]] = {} - def __init__(self, core, jid, nick): - OneToOneTab.__init__(self, core, jid) + def __init__(self, core, jid, nick, initial=None): + OneToOneTab.__init__(self, core, jid, initial) self.own_nick = nick - self.text_win = windows.TextWin() - self._text_buffer.add_window(self.text_win) self.info_header = windows.PrivateInfoWin() self.input = windows.MessageInput() # keys @@ -67,6 +73,11 @@ class PrivateTab(OneToOneTab): self.update_commands() self.update_keys() + @property + def log_name(self) -> str: + """Overriden from ChatTab because this is a case where we want the full JID""" + return self.jid.full + def remote_user_color(self): user = self.parent_muc.get_user_by_name(self.jid.resource) if user: @@ -74,14 +85,14 @@ class PrivateTab(OneToOneTab): return super().remote_user_color() @property - def general_jid(self): + def general_jid(self) -> JID: return self.jid - def get_dest_jid(self): + def get_dest_jid(self) -> JID: return self.jid @property - def nick(self): + def nick(self) -> str: return self.get_nick() def ack_message(self, msg_id: str, msg_jid: JID): @@ -103,14 +114,6 @@ class PrivateTab(OneToOneTab): def remove_information_element(plugin_name): del PrivateTab.additional_information[plugin_name] - def log_message(self, txt, nickname, time=None, typ=1): - """ - Log the messages in the archives. - """ - if not logger.log_message( - self.jid.full, nickname, txt, date=time, typ=typ): - self.core.information('Unable to write in the log file', 'Error') - def on_close(self): super().on_close() self.parent_muc.privates.remove(self) @@ -126,7 +129,7 @@ class PrivateTab(OneToOneTab): compare_users = lambda x: x.last_talked word_list = [user.nick for user in sorted(self.parent_muc.users, key=compare_users, reverse=True)\ if user.nick != self.own_nick] - after = config.get('after_completion') + ' ' + after = config.getstr('after_completion') + ' ' input_pos = self.input.pos if ' ' not in self.input.get_text()[:input_pos] or (self.input.last_completion and\ self.input.get_text()[:input_pos] == self.input.last_completion + after): @@ -139,29 +142,87 @@ class PrivateTab(OneToOneTab): and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_after) + async def handle_message(self, message: SMessage, display: bool = True): + sent = message['from'].bare == self.core.xmpp.boundjid.bare + jid = message['to'] if sent else message['from'] + with_nick = jid.resource + sender_nick = with_nick + if sent: + sender_nick = (self.own_nick or self.core.own_nick) + room_from = jid.bare + use_xhtml = config.get_by_tabname( + 'enable_xhtml_im', + jid.bare + ) + tmp_dir = get_image_cache() + if not sent: + await self.core.events.trigger_async('private_msg', message, self) + body = xhtml.get_body_from_message_stanza( + message, use_xhtml=use_xhtml, extract_images_to=tmp_dir) + if not body or not self: + return + delayed, date = common.find_delayed_tag(message) + replaced = False + user = self.parent_muc.get_user_by_name(with_nick) + if message.get_plugin('replace', check=True): + replaced_id = message['replace']['id'] + if replaced_id != '' and config.get_by_tabname( + 'group_corrections', room_from): + try: + self.modify_message( + body, + replaced_id, + message['id'], + user=user, + time=date, + jid=message['from'], + nickname=sender_nick) + replaced = True + except CorrectionError: + log.debug('Unable to correct a message', exc_info=True) + if not replaced: + msg = Message( + txt=body, + time=date, + history=delayed, + nickname=sender_nick, + nick_color=get_theme().COLOR_OWN_NICK if sent else None, + user=user, + identifier=message['id'], + jid=message['from'], + ) + if display: + self.add_message(msg) + else: + self.log_message(msg) + if sent: + self.set_last_sent_message(message, correct=replaced) + else: + self.last_remote_message = datetime.now() + @refresh_wrapper.always @command_args_parser.raw - def command_say(self, line, attention=False, correct=False): + async def command_say(self, line: str, attention: bool = False, correct: bool = False) -> None: if not self.on: return + await self._initial_log.wait() our_jid = JID(self.jid.bare) our_jid.resource = self.own_nick - msg = self.core.xmpp.make_message( + msg: SMessage = self.core.xmpp.make_message( mto=self.jid.full, mfrom=our_jid, ) msg['type'] = 'chat' msg['body'] = line + msg.enable('muc') # trigger the event BEFORE looking for colors. # This lets a plugin insert \x19xxx} colors, that will # be converted in xhtml. self.core.events.trigger('private_say', msg, self) if not msg['body']: return - user = self.parent_muc.get_user_by_name(self.own_nick) - replaced = False - if correct or msg['replace']['id']: - msg['replace']['id'] = self.last_sent_message['id'] + if correct or msg['replace']['id'] and self.last_sent_message: + msg['replace']['id'] = self.last_sent_message['id'] # type: ignore else: del msg['replace'] @@ -170,29 +231,32 @@ class PrivateTab(OneToOneTab): msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body']) msg['body'] = xhtml.clean_text(msg['body']) if config.get_by_tabname('send_chat_states', self.general_jid): - needed = 'inactive' if self.inactive else 'active' - msg['chat_state'] = needed + if self.inactive: + self.send_chat_state('inactive', always_send=True) + else: + msg['chat_state'] = 'active' if attention: msg['attention'] = True self.core.events.trigger('private_say_after', msg, self) if not msg['body']: return - self.last_sent_message = msg - self.core.handler.on_groupchat_private_message(msg, sent=True) - msg._add_receipt = True + self.set_last_sent_message(msg, correct=correct) + await self.core.handler.on_groupchat_private_message(msg, sent=True) + # Our receipts slixmpp hack + msg._add_receipt = True # type: ignore msg.send() self.cancel_paused_delay() @command_args_parser.quoted(0, 1) - def command_version(self, args): + async def command_version(self, args): """ /version """ if args: - return self.core.command.version(args[0]) + return await self.core.command.version(args[0]) jid = self.jid.full - self.core.xmpp.plugin['xep_0092'].get_version( - jid, callback=self.core.handler.on_version_result) + iq = await self.core.xmpp.plugin['xep_0092'].get_version(jid) + self.core.handler.on_version_result(iq) @command_args_parser.quoted(0, 1) def command_info(self, arg): @@ -217,8 +281,8 @@ class PrivateTab(OneToOneTab): self.text_win.resize( self.height - 2 - info_win_height - tab_win_height, self.width, 0, - 0) - self.text_win.rebuild_everything(self._text_buffer) + 0, self._text_buffer, force=self.ui_config_changed) + self.ui_config_changed = False self.info_header.resize( 1, self.width, self.height - 2 - info_win_height - tab_win_height, 0) @@ -296,9 +360,6 @@ class PrivateTab(OneToOneTab): 1, self.width, self.height - 2 - self.core.information_win_size - Tab.tab_win_height(), 0) - def get_text_window(self): - return self.text_win - @refresh_wrapper.conditional def rename_user(self, old_nick, user): """ @@ -306,16 +367,18 @@ class PrivateTab(OneToOneTab): display a message. """ self.add_message( - '\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now ' - 'known as \x19%(nick_col)s}%(new)s' % { - 'old': old_nick, - 'new': user.nick, - 'nick_col': dump_tuple(user.color), - 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) - }, - typ=2) + PersistentInfoMessage( + '\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now ' + 'known as \x19%(nick_col)s}%(new)s' % { + 'old': old_nick, + 'new': user.nick, + 'nick_col': dump_tuple(user.color), + 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT) + }, + ), + ) new_jid = self.jid.bare + '/' + user.nick - self.name = new_jid + self._name = new_jid return self.core.tabs.current_tab is self @refresh_wrapper.conditional @@ -333,28 +396,32 @@ class PrivateTab(OneToOneTab): if not status_message: self.add_message( - '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' - '%(nick)s\x19%(info_col)s} has left the room' % { - 'nick': user.nick, - 'spec': theme.CHAR_QUIT, - 'nick_col': color, - 'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR), - 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) - }, - typ=2) + PersistentInfoMessage( + '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' + '%(nick)s\x19%(info_col)s} has left the room' % { + 'nick': user.nick, + 'spec': theme.CHAR_QUIT, + 'nick_col': color, + 'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR), + 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) + }, + ), + ) else: self.add_message( - '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' - '%(nick)s\x19%(info_col)s} has left the room' - ' (%(status)s)' % { - 'status': status_message, - 'nick': user.nick, - 'spec': theme.CHAR_QUIT, - 'nick_col': color, - 'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR), - 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) - }, - typ=2) + PersistentInfoMessage( + '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}' + '%(nick)s\x19%(info_col)s} has left the room' + ' (%(status)s)' % { + 'status': status_message, + 'nick': user.nick, + 'spec': theme.CHAR_QUIT, + 'nick_col': color, + 'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR), + 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) + }, + ), + ) return self.core.tabs.current_tab is self @refresh_wrapper.conditional @@ -363,7 +430,6 @@ class PrivateTab(OneToOneTab): The user (or at least someone with the same nick) came back in the MUC """ self.activate() - self.check_features() tab = self.parent_muc theme = get_theme() color = dump_tuple(theme.COLOR_REMOTE_USER) @@ -373,26 +439,28 @@ class PrivateTab(OneToOneTab): if user: color = dump_tuple(user.color) self.add_message( - '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19' - '%(info_col)s} joined the room' % { - 'nick': nick, - 'color': color, - 'spec': theme.CHAR_JOIN, - 'join_col': dump_tuple(theme.COLOR_JOIN_CHAR), - 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) - }, - typ=2) + PersistentInfoMessage( + '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19' + '%(info_col)s} joined the room' % { + 'nick': nick, + 'color': color, + 'spec': theme.CHAR_JOIN, + 'join_col': dump_tuple(theme.COLOR_JOIN_CHAR), + 'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT) + }, + ), + ) return self.core.tabs.current_tab is self def activate(self, reason=None): self.on = True if reason: - self.add_message(txt=reason, typ=2) + self.add_message(PersistentInfoMessage(reason)) def deactivate(self, reason=None): self.on = False if reason: - self.add_message(txt=reason, typ=2) + self.add_message(PersistentInfoMessage(reason)) def matching_names(self): return [(3, self.jid.resource), (4, self.name)] @@ -402,9 +470,11 @@ class PrivateTab(OneToOneTab): error = '\x19%s}%s\x19o' % (dump_tuple(theme.COLOR_CHAR_NACK), error_message) self.add_message( - error, - highlight=True, - nickname='Error', - nick_color=theme.COLOR_ERROR_MSG, - typ=2) + Message( + error, + highlight=True, + nickname='Error', + nick_color=theme.COLOR_ERROR_MSG, + ), + ) self.core.refresh_window() |