summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/connection.py5
-rw-r--r--src/core/core.py8
-rw-r--r--src/fixes.py97
-rw-r--r--src/tabs/__init__.py2
-rw-r--r--src/tabs/basetabs.py94
-rw-r--r--src/tabs/conversationtab.py36
-rw-r--r--src/tabs/privatetab.py37
7 files changed, 99 insertions, 180 deletions
diff --git a/src/connection.py b/src/connection.py
index b06a013b..b5b7e12e 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -16,17 +16,12 @@ log = logging.getLogger(__name__)
import getpass
import sleekxmpp
from sleekxmpp.plugins.xep_0184 import XEP_0184
-from sleekxmpp.plugins.xep_0030 import StaticDisco
-from sleekxmpp.plugins.xep_0115 import StaticCaps
import common
import fixes
from common import safeJID
from config import config, options
-StaticDisco.supports = fixes.xep_30_supports
-StaticCaps.supports = fixes.xep_115_supports
-
class Connection(sleekxmpp.ClientXMPP):
"""
Receives everything from Jabber and emits the
diff --git a/src/core/core.py b/src/core/core.py
index 74930083..2e7a3c64 100644
--- a/src/core/core.py
+++ b/src/core/core.py
@@ -298,7 +298,6 @@ class Core(object):
self.on_theme_config_change)
self.add_configuration_handler("", self.on_any_config_change)
- self.reset_iq_errors()
def on_any_config_change(self, option, value):
"""
@@ -741,13 +740,6 @@ class Core(object):
self.timed_events.remove(event)
break
- def reset_iq_errors(self):
- "Reset the iq error cache periodically"
- fixes.reset_iq_errors()
- self.add_timed_event(
- timed_events.DelayedEvent(7200, self.reset_iq_errors))
-
-
####################### XMPP-related actions ##################################
def get_status(self):
diff --git a/src/fixes.py b/src/fixes.py
index cb992e88..18c117d8 100644
--- a/src/fixes.py
+++ b/src/fixes.py
@@ -10,10 +10,6 @@ from sleekxmpp.xmlstream import ET
import logging
-# used to avoid doing numerous useless disco#info requests
-# especially with message receipts
-IQ_ERRORS = set()
-
log = logging.getLogger(__name__)
def has_identity(xmpp, jid, identity):
@@ -91,97 +87,10 @@ def _filter_add_receipt_request(self, stanza):
if not stanza['body']:
return stanza
- if stanza['to'].resource:
- if not self.xmpp['xep_0030'].supports(stanza['to'],
- feature='urn:xmpp:receipts',
- cached=True):
- return stanza
+ # hack
+ if stanza['to'].resource and not hasattr(stanza, '_add_receipt'):
+ return stanza
stanza['request_receipt'] = True
return stanza
-def xep_30_supports(self, jid, node, ifrom, data):
- """
- Check if a JID supports a given feature.
-
- The data parameter may provide:
- feature -- The feature to check for support.
- local -- If true, then the query is for a JID/node
- combination handled by this Sleek instance and
- no stanzas need to be sent.
- Otherwise, a disco stanza must be sent to the
- remove JID to retrieve the info.
- cached -- If true, then look for the disco info data from
- the local cache system. If no results are found,
- send the query as usual. The self.use_cache
- setting must be set to true for this option to
- be useful. If set to false, then the cache will
- be skipped, even if a result has already been
- cached. Defaults to false.
- """
- feature = data.get('feature', None)
-
- data = {'local': data.get('local', False),
- 'cached': data.get('cached', True)}
-
- if not feature or jid.full in IQ_ERRORS:
- return False
-
- try:
- info = self.disco.get_info(jid=jid, node=node,
- ifrom=ifrom, **data)
- info = self.disco._wrap(ifrom, jid, info, True)
- features = info['disco_info']['features']
- return feature in features
- except:
- IQ_ERRORS.add(jid.full)
- log.debug('%s added to the list of entities that do'
- 'not honor disco#info', jid.full)
- return False
-
-def xep_115_supports(self, jid, node, ifrom, data):
- """
- Check if a JID supports a given feature.
-
- The data parameter may provide:
- feature -- The feature to check for support.
- local -- If true, then the query is for a JID/node
- combination handled by this Sleek instance and
- no stanzas need to be sent.
- Otherwise, a disco stanza must be sent to the
- remove JID to retrieve the info.
- cached -- If true, then look for the disco info data from
- the local cache system. If no results are found,
- send the query as usual. The self.use_cache
- setting must be set to true for this option to
- be useful. If set to false, then the cache will
- be skipped, even if a result has already been
- cached. Defaults to false.
- """
- feature = data.get('feature', None)
-
- data = {'local': data.get('local', False),
- 'cached': data.get('cached', True)}
-
- if not feature or jid.full in IQ_ERRORS:
- return False
-
- if node in (None, ''):
- info = self.caps.get_caps(jid)
- if info and feature in info['features']:
- return True
-
- try:
- info = self.disco.get_info(jid=jid, node=node,
- ifrom=ifrom, **data)
- info = self.disco._wrap(ifrom, jid, info, True)
- return feature in info['disco_info']['features']
- except:
- IQ_ERRORS.add(jid.full)
- log.debug('%s added to the list of entities that do'
- 'not honor disco#info', jid.full)
- return False
-
-def reset_iq_errors():
- "reset the iq error cache"
- IQ_ERRORS.clear()
diff --git a/src/tabs/__init__.py b/src/tabs/__init__.py
index 5f09bf38..eaf41a2f 100644
--- a/src/tabs/__init__.py
+++ b/src/tabs/__init__.py
@@ -1,4 +1,4 @@
-from . basetabs import Tab, ChatTab, GapTab
+from . basetabs import Tab, ChatTab, GapTab, OneToOneTab
from . basetabs import STATE_PRIORITY
from . rostertab import RosterInfoTab
from . muctab import MucTab, NS_MUC_USER
diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index c472bd60..6fb8a329 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -433,9 +433,6 @@ class ChatTab(Tab):
self.name = jid
self.text_win = None
self._text_buffer = TextBuffer()
- self.remote_wants_chatstates = None # change this to True or False when
- # we know that the remote user wants chatstates, or not.
- # None means we don’t know yet, and we send only "active" chatstates
self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive"
# We keep a weakref of the event that will set our chatstate to "paused", so that
# we can delete it or change it if we need to
@@ -443,7 +440,6 @@ class ChatTab(Tab):
# if that’s None, then no paused chatstate was sent recently
# if that’s a weakref returning None, then a paused chatstate was sent
# since the last input
- self.remote_supports_attention = False
# Keeps the last sent message to complete it easily in completion_correct, and to replace it.
self.last_sent_message = None
self.key_func['M-v'] = self.move_separator
@@ -503,15 +499,6 @@ class ChatTab(Tab):
identifier=identifier,
jid=jid)
- def ack_message(self, msg_id):
- """
- Ack a message
- """
- new_msg = self._text_buffer.ack_message(msg_id)
- if new_msg:
- self.text_win.modify_message(msg_id, new_msg)
- self.core.refresh_window()
-
def modify_message(self, txt, old_id, new_id, user=None, jid=None, nickname=None):
self.log_message(txt, nickname, typ=1)
message = self._text_buffer.modify_message(txt, old_id, new_id, time=time, user=user, jid=jid)
@@ -709,3 +696,84 @@ class ChatTab(Tab):
def scroll_separator(self):
self.text_win.scroll_to_separator()
+class OneToOneTab(ChatTab):
+
+ def __init__(self, jid=''):
+ ChatTab.__init__(self, jid)
+
+ # change this to True or False when
+ # we know that the remote user wants chatstates, or not.
+ # None means we don’t know yet, and we send only "active" chatstates
+ self.remote_wants_chatstates = None
+ self.remote_supports_attention = False
+ self.remote_supports_receipts = True
+ self.check_features()
+
+ def ack_message(self, msg_id):
+ """
+ Ack a message
+ """
+ new_msg = self._text_buffer.ack_message(msg_id)
+ if new_msg:
+ self.text_win.modify_message(msg_id, new_msg)
+ self.core.refresh_window()
+
+ def check_features(self):
+ "check the features supported by the other party"
+ self.core.xmpp.plugin['xep_0030'].get_info(
+ jid=self.get_dest_jid(), block=False, timeout=5,
+ callback=self.features_checked)
+
+ def command_attention(self, message=''):
+ "/attention [message]"
+ if message is not '':
+ self.command_say(message, attention=True)
+ else:
+ msg = self.core.xmpp.make_message(self.get_dest_jid())
+ msg['type'] = 'chat'
+ msg['attention'] = True
+ msg.send()
+
+ def command_say(self, line, correct=False, attention=False):
+ pass
+
+ def _feature_attention(self, features):
+ "Check for the 'attention' features"
+ if 'urn:xmpp:attention:0' in features:
+ self.remote_supports_attention = True
+ self.register_command('attention', self.command_attention,
+ usage=_('[message]'),
+ shortdesc=_('Request the attention.'),
+ desc=_('Attention: Request the attention of '
+ 'the contact. Can also send a message'
+ ' along with the attention.'))
+ else:
+ self.remote_supports_attention = False
+
+ def _feature_correct(self, features):
+ "Check for the 'correction' feature"
+ if not 'urn:xmpp:message-correct:0' in features:
+ if 'correct' in self.commands:
+ del self.commands['correct']
+ elif not 'correct' in self.commands:
+ self.register_command('correct', self.command_correct,
+ desc=_('Fix the last message with whatever you want.'),
+ shortdesc=_('Correct the last message.'),
+ completion=self.completion_correct)
+
+ def _feature_receipts(self, features):
+ "Check for the 'receipts' feature"
+ if 'urn:xmpp:receipts' in features:
+ self.remote_supports_receipts = True
+ else:
+ self.remote_supports_receipts = False
+
+ def features_checked(self, iq):
+ "Features check callback"
+ features = iq['disco_info'].get_features() or []
+ log.debug('\n\nFEATURES:\n%s\n\n%s\n\n', iq, features)
+ self._feature_correct(features)
+ self._feature_attention(features)
+ self._feature_receipts(features)
+
+
diff --git a/src/tabs/conversationtab.py b/src/tabs/conversationtab.py
index 7480906b..e6324182 100644
--- a/src/tabs/conversationtab.py
+++ b/src/tabs/conversationtab.py
@@ -18,7 +18,7 @@ log = logging.getLogger(__name__)
import curses
-from . import ChatTab, Tab
+from . basetabs import OneToOneTab, Tab
import common
import fixes
@@ -30,7 +30,7 @@ from decorators import refresh_wrapper
from roster import roster
from theming import get_theme, dump_tuple
-class ConversationTab(ChatTab):
+class ConversationTab(OneToOneTab):
"""
The tab containg a normal conversation (not from a MUC)
Must not be instantiated, use Static or Dynamic version only.
@@ -40,7 +40,7 @@ class ConversationTab(ChatTab):
additional_informations = {}
message_type = 'chat'
def __init__(self, jid):
- ChatTab.__init__(self, jid)
+ OneToOneTab.__init__(self, jid)
self.nick = None
self.nick_sent = False
self.state = 'normal'
@@ -49,7 +49,6 @@ class ConversationTab(ChatTab):
self._text_buffer.add_window(self.text_win)
self.upper_bar = windows.ConversationStatusMessageWin()
self.input = windows.MessageInput()
- self.check_attention()
# keys
self.key_func['^I'] = self.completion
# commands
@@ -142,6 +141,8 @@ class ConversationTab(ChatTab):
typ=1)
self.last_sent_message = msg
+ if self.remote_supports_receipts:
+ msg._add_receipt = True
msg.send()
self.cancel_paused_delay()
self.text_win.refresh()
@@ -208,32 +209,6 @@ class ConversationTab(ChatTab):
self._text_buffer.add_message("\x19%(info_col)s}No information available\x19o" % {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)})
return True
-
- def command_attention(self, message=''):
- if message is not '':
- self.command_say(message, attention=True)
- else:
- msg = self.core.xmpp.make_message(self.get_dest_jid())
- msg['type'] = 'chat'
- msg['attention'] = True
- msg.send()
-
- def check_attention(self):
- self.core.xmpp.plugin['xep_0030'].get_info(
- jid=self.get_dest_jid(), block=False, timeout=5,
- callback=self.on_attention_checked)
-
- def on_attention_checked(self, iq):
- if 'urn:xmpp:attention:0' in iq['disco_info'].get_features():
- self.core.information('Attention is supported', 'Info')
- self.remote_supports_attention = True
- self.commands['attention'] = (self.command_attention,
- _('Usage: /attention [message]\nAttention: Require'
- ' the attention of the contact. Can also send a '
- 'message along with the attention.'), None)
- else:
- self.remote_supports_attention = False
-
def command_unquery(self, arg):
self.core.close_tab()
@@ -416,6 +391,7 @@ class DynamicConversationTab(ConversationTab):
self.name,
resource)
self.add_message(message, typ=0)
+ self.check_features()
def unlock_command(self, arg=None):
self.unlock()
diff --git a/src/tabs/privatetab.py b/src/tabs/privatetab.py
index b8699bd2..6ab06744 100644
--- a/src/tabs/privatetab.py
+++ b/src/tabs/privatetab.py
@@ -17,7 +17,7 @@ log = logging.getLogger(__name__)
import curses
-from . import ChatTab, MucTab, Tab
+from . import OneToOneTab, MucTab, Tab
import fixes
import windows
@@ -28,7 +28,7 @@ from decorators import refresh_wrapper
from logger import logger
from theming import get_theme, dump_tuple
-class PrivateTab(ChatTab):
+class PrivateTab(OneToOneTab):
"""
The tab containg a private conversation (someone from a MUC)
"""
@@ -37,14 +37,13 @@ class PrivateTab(ChatTab):
additional_informations = {}
plugin_keys = {}
def __init__(self, name, nick):
- ChatTab.__init__(self, name)
+ OneToOneTab.__init__(self, name)
self.own_nick = nick
self.name = name
self.text_win = windows.TextWin()
self._text_buffer.add_window(self.text_win)
self.info_header = windows.PrivateInfoWin()
self.input = windows.MessageInput()
- self.check_attention()
# keys
self.key_func['^I'] = self.completion
# commands
@@ -68,6 +67,9 @@ class PrivateTab(ChatTab):
def general_jid(self):
return self.name
+ def get_dest_jid(self):
+ return self.name
+
@property
def nick(self):
return self.get_nick()
@@ -172,36 +174,13 @@ class PrivateTab(ChatTab):
typ=1)
self.last_sent_message = msg
+ if self.remote_supports_receipts:
+ msg._add_receipt = True
msg.send()
self.cancel_paused_delay()
self.text_win.refresh()
self.input.refresh()
- def command_attention(self, message=''):
- if message is not '':
- self.command_say(message, attention=True)
- else:
- msg = self.core.xmpp.make_message(self.name)
- msg['type'] = 'chat'
- msg['attention'] = True
- msg.send()
-
- def check_attention(self):
- self.core.xmpp.plugin['xep_0030'].get_info(jid=self.name, block=False, timeout=5, callback=self.on_attention_checked)
-
- def on_attention_checked(self, iq):
- if 'urn:xmpp:attention:0' in iq['disco_info'].get_features():
- self.core.information('Attention is supported', 'Info')
- self.remote_supports_attention = True
- self.commands['attention'] = (
- self.command_attention,
- _('Usage: /attention [message]\nAttention:'
- 'Require the attention of the contact. Can'
- ' also send a message along with the attention.'),
- None)
- else:
- self.remote_supports_attention = False
-
def command_unquery(self, arg):
"""
/unquery