Commit 2b76f72e authored by mathieui's avatar mathieui

Fix the message doubling situation

Add an "initial" parameter for onetoonetabs, and delay the reception of
the message until the sync is done (and possibly ignore the message if
it is fetched by the sync).

Fixes #3542
parent 3035f4ed
Pipeline #3970 passed with stages
in 9 minutes and 12 seconds
......@@ -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):
......
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment