diff options
Diffstat (limited to 'poezio/tabs/muctab.py')
-rw-r--r-- | poezio/tabs/muctab.py | 170 |
1 files changed, 158 insertions, 12 deletions
diff --git a/poezio/tabs/muctab.py b/poezio/tabs/muctab.py index acc145af..e2d546c9 100644 --- a/poezio/tabs/muctab.py +++ b/poezio/tabs/muctab.py @@ -18,6 +18,7 @@ import random import re import functools from copy import copy +from dataclasses import dataclass from datetime import datetime from typing import ( cast, @@ -44,12 +45,13 @@ from poezio import timed_events from poezio import windows from poezio import xhtml from poezio.common import to_utc -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, command_args_parser from poezio.logger import logger from poezio.log_loader import LogLoader, MAMFiller from poezio.roster import roster +from poezio.text_buffer import CorrectionError from poezio.theming import get_theme, dump_tuple from poezio.user import User from poezio.core.structs import Completion, Status @@ -73,6 +75,18 @@ NS_MUC_USER = 'http://jabber.org/protocol/muc#user' COMPARE_USERS_LAST_TALKED = lambda x: x.last_talked +@dataclass +class MessageData: + message: SMessage + delayed: bool + date: Optional[datetime] + nick: str + user: Optional[User] + room_from: str + body: str + is_history: bool + + class MucTab(ChatTab): """ The tab containing a multi-user-chat room. @@ -154,14 +168,14 @@ class MucTab(ChatTab): """ The user do not want to send their config, send an iq cancel """ - asyncio.ensure_future(self.core.xmpp['xep_0045'].cancel_config(self.jid)) + asyncio.create_task(self.core.xmpp['xep_0045'].cancel_config(self.jid)) self.core.close_tab() def send_config(self, form: Form) -> None: """ The user sends their config to the server """ - asyncio.ensure_future(self.core.xmpp['xep_0045'].set_room_config(self.jid, form)) + asyncio.create_task(self.core.xmpp['xep_0045'].set_room_config(self.jid, form)) self.core.close_tab() def join(self) -> None: @@ -233,6 +247,8 @@ class MucTab(ChatTab): message) self.core.disable_private_tabs(self.jid.bare, reason=msg) else: + self.presence_buffer = [] + self.users = [] muc.leave_groupchat(self.core.xmpp, self.jid, self.own_nick, message) @@ -450,9 +466,6 @@ class MucTab(ChatTab): # TODO: send the disco#info identity name here, if it exists. return self.jid.node - def get_text_window(self) -> windows.TextWin: - return self.text_win - def on_lose_focus(self) -> None: if self.joined: if self.input.text: @@ -480,6 +493,126 @@ class MucTab(ChatTab): self.general_jid) and not self.input.get_text(): self.send_chat_state('active') + async def handle_message(self, message: SMessage) -> bool: + """Parse an incoming message + + Returns False if the message was dropped silently. + """ + room_from = message['from'].bare + nick_from = message['mucnick'] + user = self.get_user_by_name(nick_from) + if user and user in self.ignores: + return False + + await self.core.events.trigger_async('muc_msg', message, self) + use_xhtml = config.get_by_tabname('enable_xhtml_im', room_from) + tmp_dir = get_image_cache() + body = xhtml.get_body_from_message_stanza( + message, use_xhtml=use_xhtml, extract_images_to=tmp_dir) + + # TODO: #3314. Is this a MUC reflection? + # Is this an encrypted message? Is so ignore. + # It is not possible in the OMEMO case to decrypt these messages + # since we don't encrypt for our own device (something something + # forward secrecy), but even for non-FS encryption schemes anyway + # messages shouldn't have changed after a round-trip to the room. + # Otherwire replace the matching message we sent. + if not body: + return False + + old_state = self.state + delayed, date = common.find_delayed_tag(message) + is_history = not self.joined and delayed + + mdata = MessageData( + message, delayed, date, nick_from, user, room_from, body, + is_history + ) + + replaced = False + if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None: + replaced = await self._handle_correction_message(mdata) + if not replaced: + await self._handle_normal_message(mdata) + if mdata.nick == self.own_nick: + self.set_last_sent_message(message, correct=replaced) + self._refresh_after_message(old_state) + return True + + def _refresh_after_message(self, old_state: str) -> None: + """Refresh the appropriate UI after a message is received""" + if self is self.core.tabs.current_tab: + self.refresh() + elif self.state != old_state: + self.core.refresh_tab_win() + current = self.core.tabs.current_tab + current.refresh_input() + self.core.doupdate() + + async def _handle_correction_message(self, message: MessageData) -> bool: + """Process a correction message. + + Returns true if a message was actually corrected. + """ + replaced_id = message.message['replace']['id'] + if replaced_id != '' and config.get_by_tabname( + 'group_corrections', JID(message.room_from)): + try: + delayed_date = message.date or datetime.now() + modify_hl = self.modify_message( + message.body, + replaced_id, + message.message['id'], + time=delayed_date, + delayed=message.delayed, + nickname=message.nick, + user=message.user + ) + if modify_hl: + await self.core.events.trigger_async( + 'highlight', + message.message, + self + ) + return True + except CorrectionError: + log.debug('Unable to correct a message', exc_info=True) + return False + + async def _handle_normal_message(self, message: MessageData) -> None: + """ + Process the non-correction groupchat message. + """ + ui_msg: Union[InfoMessage, Message] + # Messages coming from MUC barejid (Server maintenance, IRC mode + # changes from biboumi, etc.) have no nick/resource and are displayed + # as info messages. + highlight = False + if message.nick: + highlight = self.message_is_highlight( + message.body, message.nick, message.is_history + ) + ui_msg = Message( + txt=message.body, + time=message.date, + nickname=message.nick, + history=message.is_history, + delayed=message.delayed, + identifier=message.message['id'], + jid=message.message['from'], + user=message.user, + highlight=highlight, + ) + else: + ui_msg = InfoMessage( + txt=message.body, + time=message.date, + identifier=message.message['id'], + ) + self.add_message(ui_msg) + if highlight: + await self.core.events.trigger_async('highlight', message, self) + def handle_presence(self, presence: Presence) -> None: """Handle MUC presence""" self.reset_lag() @@ -610,7 +743,7 @@ class MucTab(ChatTab): }, ), ) - asyncio.ensure_future(LogLoader( + asyncio.create_task(LogLoader( logger, self, config.get_by_tabname('use_log', self.general_jid) ).tab_open()) @@ -662,6 +795,17 @@ class MucTab(ChatTab): elif typ == 'unavailable': self.on_user_leave_groupchat(user, jid, status, from_nick, JID(from_room), server_initiated) + ns = 'http://jabber.org/protocol/muc#user' + if presence.xml.find(f'{{{ns}}}x/{{{ns}}}destroy') is not None: + info = f'Room {self.jid} was destroyed.' + if presence['muc']['destroy']: + reason = presence['muc']['destroy']['reason'] + altroom = presence['muc']['destroy']['jid'] + if reason: + info += f' “{reason}”.' + if altroom: + info += f' The new address now is {altroom}.' + self.core.information(info, 'Info') # status change else: self.on_user_change_status(user, from_nick, from_room, affiliation, @@ -1513,7 +1657,7 @@ class MucTab(ChatTab): bookmark = self.core.bookmarks[self.jid] if bookmark: bookmark.autojoin = False - asyncio.ensure_future( + asyncio.create_task( self.core.bookmarks.save(self.core.xmpp) ) self.core.close_tab(self) @@ -1538,8 +1682,10 @@ class MucTab(ChatTab): r = self.core.open_private_window(self.jid.bare, user.nick) if r and len(args) == 2: msg = args[1] - r.command_say( - xhtml.convert_simple_to_full_colors(msg) + asyncio.ensure_future( + r.command_say( + xhtml.convert_simple_to_full_colors(msg) + ) ) if not r: self.core.information("Cannot find user: %s" % nick, 'Error') @@ -1712,7 +1858,7 @@ class MucTab(ChatTab): return None @command_args_parser.raw - def command_say(self, line: str, attention: bool = False, correct: bool = False): + async def command_say(self, line: str, attention: bool = False, correct: bool = False): """ /say <message> Or normal input + enter @@ -2198,7 +2344,7 @@ class MucTab(ChatTab): 'shortdesc': 'Fix a color for a nick.', 'completion': - self.completion_recolor + self.completion_color }, { 'name': 'cycle', |