summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--poezio/core/handlers.py142
-rw-r--r--poezio/log_loader.py2
-rw-r--r--poezio/tabs/basetabs.py17
-rw-r--r--poezio/tabs/conversationtab.py88
-rw-r--r--poezio/tabs/privatetab.py66
5 files changed, 183 insertions, 132 deletions
diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py
index 6262a1d5..d4625b4b 100644
--- a/poezio/core/handlers.py
+++ b/poezio/core/handlers.py
@@ -346,95 +346,34 @@ class HandlerCore:
use_xhtml = config.get_by_tabname('enable_xhtml_im',
message['from'].bare)
tmp_dir = get_image_cache()
- body = xhtml.get_body_from_message_stanza(
- message, use_xhtml=use_xhtml, extract_images_to=tmp_dir)
- if not body:
+ if not xhtml.get_body_from_message_stanza(
+ message, use_xhtml=use_xhtml, extract_images_to=tmp_dir):
if not self.core.xmpp.plugin['xep_0380'].has_eme(message):
return
self.core.xmpp.plugin['xep_0380'].replace_body_with_eme(message)
- body = message['body']
- remote_nick = ''
# normal message, we are the recipient
if message['to'].bare == self.core.xmpp.boundjid.bare:
conv_jid = message['from']
- jid = conv_jid
- color = get_theme().COLOR_REMOTE_USER
- # check for a name
- if conv_jid.bare in roster:
- remote_nick = roster[conv_jid.bare].name
- # check for a received nick
- if not remote_nick and config.getbool('enable_user_nick'):
- if message.xml.find(
- '{http://jabber.org/protocol/nick}nick') is not None:
- remote_nick = message['nick']['nick']
- if not remote_nick:
- remote_nick = conv_jid.user
- if not remote_nick:
- remote_nick = conv_jid.full
own = False
# we wrote the message (happens with carbons)
elif message['from'].bare == self.core.xmpp.boundjid.bare:
conv_jid = message['to']
- jid = self.core.xmpp.boundjid
- color = get_theme().COLOR_OWN_NICK
- remote_nick = self.core.own_nick
own = True
# we are not part of that message, drop it
else:
return
- conversation = self.core.get_conversation_by_jid(conv_jid, create=True)
- if isinstance(conversation,
- tabs.DynamicConversationTab) and conv_jid.resource:
- conversation.lock(conv_jid.resource)
-
- if not own:
- if not conversation.nick:
- conversation.nick = remote_nick
- else:
- remote_nick = conversation.get_nick()
-
- conversation.last_remote_message = datetime.now()
- self.core.events.trigger('conversation_msg', message, conversation)
-
- if not message['body']:
- return
- body = xhtml.get_body_from_message_stanza(
- message, use_xhtml=use_xhtml, extract_images_to=tmp_dir)
- delayed, date = common.find_delayed_tag(message)
-
- def try_modify():
- if message.xml.find('{urn:xmpp:message-correct:0}replace') is None:
- return False
- replaced_id = message['replace']['id']
- if replaced_id and config.get_by_tabname('group_corrections',
- conv_jid.bare):
- try:
- conversation.modify_message(
- body,
- replaced_id,
- message['id'],
- time=date,
- jid=jid,
- nickname=remote_nick)
- return True
- except CorrectionError:
- log.debug('Unable to correct a message', exc_info=True)
- return False
-
- if not try_modify():
- conversation.add_message(
- PMessage(
- txt=body,
- time=date,
- nickname=remote_nick,
- nick_color=color,
- history=delayed,
- identifier=message['id'],
- jid=jid,
- )
+ conversation = self.core.get_conversation_by_jid(conv_jid, create=False)
+ if conversation is None:
+ conversation = tabs.DynamicConversationTab(
+ self.core,
+ JID(conv_jid.bare),
+ initial=message,
)
+ self.core.tabs.append(conversation)
+ else:
+ conversation.handle_message(message)
if not own and 'private' in config.getstr('beep_on').split():
if not config.get_by_tabname('disable_beep', conv_jid.bare):
@@ -679,7 +618,10 @@ class HandlerCore:
return
room_from = jid.bare
- use_xhtml = config.get_by_tabname('enable_xhtml_im', jid.bare)
+ use_xhtml = config.get_by_tabname(
+ 'enable_xhtml_im',
+ jid.bare
+ )
tmp_dir = get_image_cache()
body = xhtml.get_body_from_message_stanza(
message, use_xhtml=use_xhtml, extract_images_to=tmp_dir)
@@ -687,14 +629,6 @@ class HandlerCore:
jid.full,
tabs.PrivateTab) # get the tab with the private conversation
ignore = config.get_by_tabname('ignore_private', room_from)
- if not tab: # It's the first message we receive: create the tab
- if body and not ignore:
- tab = self.core.open_private_window(room_from, with_nick,
- False)
- # Tab can still be None here, when receiving carbons of a MUC-PM for
- # example
- sender_nick = (tab and tab.own_nick
- or self.core.own_nick) if sent else with_nick
if ignore and not sent:
self.core.events.trigger('ignored_private', message, tab)
msg = config.get_by_tabname('private_auto_response', room_from)
@@ -702,44 +636,18 @@ class HandlerCore:
self.core.xmpp.send_message(
mto=jid.full, mbody=msg, mtype='chat')
return
- if not sent:
- self.core.events.trigger('private_msg', message, tab)
- body = xhtml.get_body_from_message_stanza(
- message, use_xhtml=use_xhtml, extract_images_to=tmp_dir)
- if not body or not tab:
- return
- replaced = False
- user = tab.parent_muc.get_user_by_name(with_nick)
- if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None:
- replaced_id = message['replace']['id']
- if replaced_id != '' and config.get_by_tabname(
- 'group_corrections', room_from):
- try:
- tab.modify_message(
- body,
- replaced_id,
- message['id'],
- user=user,
- jid=message['from'],
- nickname=sender_nick)
- replaced = True
- except CorrectionError:
- log.debug('Unable to correct a message', exc_info=True)
- if not replaced:
- tab.add_message(
- PMessage(
- txt=body,
- nickname=sender_nick,
- nick_color=get_theme().COLOR_OWN_NICK if sent else None,
- user=user,
- identifier=message['id'],
- jid=message['from'],
+ if tab is None: # It's the first message we receive: create the tab
+ if body and not ignore:
+ tab = tabs.PrivateTab(
+ self.core,
+ jid,
+ self.core.own_nick,
+ initial=message,
)
- )
- if sent:
- tab.set_last_sent_message(message, correct=replaced)
+ self.core.tabs.append(tab)
+ tab.parent_muc.privates.append(tab)
else:
- tab.last_remote_message = datetime.now()
+ tab.handle_message(message)
if not sent and 'private' in config.getstr('beep_on').split():
if not config.get_by_tabname('disable_beep', jid.full):
diff --git a/poezio/log_loader.py b/poezio/log_loader.py
index 8fd0ed82..aa645c30 100644
--- a/poezio/log_loader.py
+++ b/poezio/log_loader.py
@@ -309,6 +309,7 @@ class MAMFiller:
self.future = asyncio.ensure_future(self.fetch_routine())
self.done = asyncio.Event()
self.limit = limit
+ self.result = 0
def cancel(self) -> None:
"""Cancel the routine and signal the end."""
@@ -334,6 +335,7 @@ class MAMFiller:
'Fetched %s messages to fill local logs for %s',
len(messages), self.tab.jid,
)
+ self.result = len(messages)
except NoMAMSupportException:
log.debug('The entity %s does not support MAM', self.tab.jid)
return
diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py
index 43ab237f..f52122a5 100644
--- a/poezio/tabs/basetabs.py
+++ b/poezio/tabs/basetabs.py
@@ -999,7 +999,7 @@ class ChatTab(Tab):
class OneToOneTab(ChatTab):
- def __init__(self, core, jid):
+ def __init__(self, core, jid, initial=None):
ChatTab.__init__(self, core, jid)
self.__status = Status("", "")
@@ -1018,18 +1018,29 @@ class OneToOneTab(ChatTab):
shortdesc='Request the attention.',
desc='Attention: Request the attention of the contact. Can also '
'send a message along with the attention.')
- self.init_logs()
+ self.init_logs(initial=initial)
- def init_logs(self) -> None:
+ def init_logs(self, initial=None) -> None:
use_log = config.get_by_tabname('use_log', self.jid)
mam_sync = config.get_by_tabname('mam_sync', self.jid)
if use_log and mam_sync:
limit = config.get_by_tabname('mam_sync_limit', self.jid)
self.mam_filler = MAMFiller(logger, self, limit)
+
+ async def fallback_no_mam():
+ await self.mam_filler.done.wait()
+ if self.mam_filler.result == 0:
+ self.handle_message(initial)
+ asyncio.ensure_future(fallback_no_mam())
+ elif use_log and initial:
+ self.handle_message(initial, display=False)
asyncio.ensure_future(
LogLoader(logger, self, use_log).tab_open()
)
+ def handle_message(self, msg: SMessage, display: bool = True):
+ pass
+
def remote_user_color(self):
return dump_tuple(get_theme().COLOR_REMOTE_USER)
diff --git a/poezio/tabs/conversationtab.py b/poezio/tabs/conversationtab.py
index 78faf49a..eb6e97b5 100644
--- a/poezio/tabs/conversationtab.py
+++ b/poezio/tabs/conversationtab.py
@@ -13,22 +13,25 @@ There are two different instances of a ConversationTab:
"""
import curses
import logging
+from datetime import datetime
from typing import Dict, Callable
-from slixmpp import JID, InvalidJID
+from slixmpp import JID, InvalidJID, Message as SMessage
from poezio.tabs.basetabs import OneToOneTab, Tab
from poezio import common
+from poezio import tabs
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.roster import roster
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
-from poezio.ui.types import InfoMessage
+from poezio.ui.types import InfoMessage, Message
+from poezio.text_buffer import CorrectionError
log = logging.getLogger(__name__)
@@ -43,8 +46,8 @@ class ConversationTab(OneToOneTab):
additional_information: Dict[str, Callable[[str], str]] = {}
message_type = 'chat'
- def __init__(self, core, jid):
- OneToOneTab.__init__(self, core, jid)
+ def __init__(self, core, jid, initial=None):
+ OneToOneTab.__init__(self, core, jid, initial=initial)
self.nick = None
self.nick_sent = False
self.state = 'normal'
@@ -102,6 +105,71 @@ class ConversationTab(OneToOneTab):
def completion(self):
self.complete_commands(self.input)
+ def handle_message(self, message: SMessage, display: bool = True):
+ """Handle a received message.
+
+ The message can come from us (carbon copy).
+ """
+ use_xhtml = config.get_by_tabname(
+ 'enable_xhtml_im',
+ message['from'].bare
+ )
+ tmp_dir = get_image_cache()
+ # normal message, we are the recipient
+ if message['to'].bare == self.core.xmpp.boundjid.bare:
+ conv_jid = message['from']
+ jid = conv_jid
+ color = get_theme().COLOR_REMOTE_USER
+ self.last_remote_message = datetime.now()
+ remote_nick = self.get_nick()
+ # we wrote the message (happens with carbons)
+ elif message['from'].bare == self.core.xmpp.boundjid.bare:
+ conv_jid = message['to']
+ jid = self.core.xmpp.boundjid
+ color = get_theme().COLOR_OWN_NICK
+ remote_nick = self.core.own_nick
+ # we are not part of that message, drop it
+ else:
+ return
+
+ self.core.events.trigger('conversation_msg', message, self)
+
+ if not message['body']:
+ return
+ body = xhtml.get_body_from_message_stanza(
+ message, use_xhtml=use_xhtml, extract_images_to=tmp_dir)
+ delayed, date = common.find_delayed_tag(message)
+
+ replaced = False
+ if message.get_plugin('replace', check=True):
+ replaced_id = message['replace']['id']
+ if replaced_id and config.get_by_tabname('group_corrections',
+ conv_jid.bare):
+ try:
+ replaced = self.modify_message(
+ body,
+ replaced_id,
+ message['id'],
+ time=date,
+ jid=jid,
+ nickname=remote_nick)
+ except CorrectionError:
+ log.debug('Unable to correct the message: %s', message)
+ if not replaced:
+ msg = Message(
+ txt=body,
+ time=date,
+ nickname=remote_nick,
+ nick_color=color,
+ history=delayed,
+ identifier=message['id'],
+ jid=jid,
+ )
+ if display:
+ self.add_message(msg)
+ else:
+ self.log_message(msg)
+
@refresh_wrapper.always
@command_args_parser.raw
def command_say(self, line: str, attention: bool = False, correct: bool = False):
@@ -396,9 +464,9 @@ class DynamicConversationTab(ConversationTab):
plugin_commands: Dict[str, Command] = {}
plugin_keys: Dict[str, Callable] = {}
- def __init__(self, core, jid, resource=None):
+ def __init__(self, core, jid, initial=None):
self.locked_resource = None
- ConversationTab.__init__(self, core, jid)
+ ConversationTab.__init__(self, core, jid, initial=initial)
self.jid.resource = None
self.info_header = windows.DynamicConversationInfoWin()
self.register_command(
@@ -466,15 +534,15 @@ class StaticConversationTab(ConversationTab):
plugin_commands: Dict[str, Command] = {}
plugin_keys: Dict[str, Callable] = {}
- def __init__(self, core, jid):
- ConversationTab.__init__(self, core, jid)
+ def __init__(self, core, jid, initial=None):
+ ConversationTab.__init__(self, core, jid, initial=initial)
assert jid.resource
self.info_header = windows.ConversationInfoWin()
self.resize()
self.update_commands()
self.update_keys()
- def init_logs(self) -> None:
+ def init_logs(self, initial=None) -> None:
# Disable local logs becauseā€¦
pass
diff --git a/poezio/tabs/privatetab.py b/poezio/tabs/privatetab.py
index daf81733..439ab5f8 100644
--- a/poezio/tabs/privatetab.py
+++ b/poezio/tabs/privatetab.py
@@ -12,19 +12,23 @@ the ConversationTab (such as tab-completion on nicks from the room).
"""
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.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,
@@ -42,7 +46,7 @@ class PrivateTab(OneToOneTab):
message_type = 'chat'
additional_information: Dict[str, Callable[[str], str]] = {}
- def __init__(self, core, jid, nick):
+ def __init__(self, core, jid, nick, initial=None):
OneToOneTab.__init__(self, core, jid)
self.own_nick = nick
self.info_header = windows.PrivateInfoWin()
@@ -137,6 +141,64 @@ class PrivateTab(OneToOneTab):
and not self.input.get_text().startswith('//'))
self.send_composing_chat_state(empty_after)
+ 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:
+ self.core.events.trigger('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: str, attention: bool = False, correct: bool = False) -> None: