cean chatstate code: remove XEP-0022 support and use caps to know if a contact supports it
This commit is contained in:
parent
36af66b063
commit
98432f6666
11 changed files with 109 additions and 212 deletions
|
@ -135,27 +135,17 @@ class BannerTweaksPlugin(GajimPlugin):
|
||||||
if cs and st in ('composing_only', 'all'):
|
if cs and st in ('composing_only', 'all'):
|
||||||
if contact.show == 'offline':
|
if contact.show == 'offline':
|
||||||
chatstate = ''
|
chatstate = ''
|
||||||
elif contact.composing_xep == 'XEP-0085':
|
elif st == 'all' or cs == 'composing':
|
||||||
if st == 'all' or cs == 'composing':
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
|
||||||
else:
|
|
||||||
chatstate = ''
|
|
||||||
elif contact.composing_xep == 'XEP-0022':
|
|
||||||
if cs in ('composing', 'paused'):
|
|
||||||
# only print composing, paused
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
|
||||||
else:
|
|
||||||
chatstate = ''
|
|
||||||
else:
|
|
||||||
# When does that happen ? See [7797] and [7804]
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
chatstate = helpers.get_uf_chatstate(cs)
|
||||||
|
else:
|
||||||
|
chatstate = ''
|
||||||
|
|
||||||
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
|
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
|
||||||
(font_attrs, name, font_attrs_small, acct_info, chatstate)
|
(font_attrs, name, font_attrs_small, acct_info, chatstate)
|
||||||
else:
|
else:
|
||||||
# weight="heavy" size="x-large"
|
# weight="heavy" size="x-large"
|
||||||
label_text = '<span %s>%s</span><span %s>%s</span>' % \
|
label_text = '<span %s>%s</span><span %s>%s</span>' % \
|
||||||
(font_attrs, name, font_attrs_small, acct_info)
|
(font_attrs, name, font_attrs_small, acct_info)
|
||||||
|
|
||||||
banner_name_label.set_markup(label_text)
|
banner_name_label.set_markup(label_text)
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ from common.pep import MOODS, ACTIVITIES
|
||||||
from common.xmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
|
from common.xmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
|
||||||
from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
|
from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
|
||||||
from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_ICE_UDP
|
from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_ICE_UDP
|
||||||
|
from common.xmpp.protocol import NS_CHATSTATES
|
||||||
from common.connection_handlers_events import MessageOutgoingEvent
|
from common.connection_handlers_events import MessageOutgoingEvent
|
||||||
|
|
||||||
from command_system.implementation.middleware import ChatCommandProcessor
|
from command_system.implementation.middleware import ChatCommandProcessor
|
||||||
|
@ -850,8 +851,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def send_message(self, message, keyID='', type_='chat', chatstate=None,
|
def send_message(self, message, keyID='', type_='chat', chatstate=None,
|
||||||
msg_id=None, composing_xep=None, resource=None, xhtml=None, callback=None,
|
msg_id=None, resource=None, xhtml=None, callback=None, callback_args=[],
|
||||||
callback_args=[], process_commands=True):
|
process_commands=True):
|
||||||
"""
|
"""
|
||||||
Send the given message to the active tab. Doesn't return None if error
|
Send the given message to the active tab. Doesn't return None if error
|
||||||
"""
|
"""
|
||||||
|
@ -866,9 +867,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
||||||
account=self.account, jid=self.contact.jid, message=message,
|
account=self.account, jid=self.contact.jid, message=message,
|
||||||
keyID=keyID, type_=type_, chatstate=chatstate, msg_id=msg_id,
|
keyID=keyID, type_=type_, chatstate=chatstate, msg_id=msg_id,
|
||||||
composing_xep=composing_xep, resource=resource,
|
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
|
||||||
user_nick=self.user_nick, xhtml=xhtml, label=label,
|
label=label, callback=callback, callback_args=callback_args,
|
||||||
callback=callback, callback_args=callback_args, control=self))
|
control=self))
|
||||||
|
|
||||||
# Record the history of sent messages
|
# Record the history of sent messages
|
||||||
self.save_message(message, 'sent')
|
self.save_message(message, 'sent')
|
||||||
|
@ -2037,24 +2038,13 @@ class ChatControl(ChatControlBase):
|
||||||
if cs and st in ('composing_only', 'all'):
|
if cs and st in ('composing_only', 'all'):
|
||||||
if contact.show == 'offline':
|
if contact.show == 'offline':
|
||||||
chatstate = ''
|
chatstate = ''
|
||||||
elif contact.composing_xep == 'XEP-0085':
|
elif st == 'all' or cs == 'composing':
|
||||||
if st == 'all' or cs == 'composing':
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
|
||||||
else:
|
|
||||||
chatstate = ''
|
|
||||||
elif contact.composing_xep == 'XEP-0022':
|
|
||||||
if cs in ('composing', 'paused'):
|
|
||||||
# only print composing, paused
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
|
||||||
else:
|
|
||||||
chatstate = ''
|
|
||||||
else:
|
|
||||||
# When does that happen ? See [7797] and [7804]
|
|
||||||
chatstate = helpers.get_uf_chatstate(cs)
|
chatstate = helpers.get_uf_chatstate(cs)
|
||||||
|
else:
|
||||||
|
chatstate = ''
|
||||||
|
|
||||||
label_text = '<span %s>%s</span><span %s>%s %s</span>' \
|
label_text = '<span %s>%s</span><span %s>%s %s</span>' \
|
||||||
% (font_attrs, name, font_attrs_small,
|
% (font_attrs, name, font_attrs_small, acct_info, chatstate)
|
||||||
acct_info, chatstate)
|
|
||||||
if acct_info:
|
if acct_info:
|
||||||
acct_info = ' ' + acct_info
|
acct_info = ' ' + acct_info
|
||||||
label_tooltip = '%s%s %s' % (name, acct_info, chatstate)
|
label_tooltip = '%s%s %s' % (name, acct_info, chatstate)
|
||||||
|
@ -2210,7 +2200,7 @@ class ChatControl(ChatControlBase):
|
||||||
dialogs.ESessionInfoWindow(self.session)
|
dialogs.ESessionInfoWindow(self.session)
|
||||||
|
|
||||||
def send_message(self, message, keyID='', chatstate=None, xhtml=None,
|
def send_message(self, message, keyID='', chatstate=None, xhtml=None,
|
||||||
process_commands=True):
|
process_commands=True):
|
||||||
"""
|
"""
|
||||||
Send a message to contact
|
Send a message to contact
|
||||||
"""
|
"""
|
||||||
|
@ -2234,25 +2224,9 @@ class ChatControl(ChatControlBase):
|
||||||
|
|
||||||
chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
|
chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
|
||||||
'disabled'
|
'disabled'
|
||||||
composing_xep = contact.composing_xep
|
|
||||||
chatstate_to_send = None
|
chatstate_to_send = None
|
||||||
if chatstates_on and contact is not None:
|
if chatstates_on and contact is not None:
|
||||||
if composing_xep is None:
|
if contact.supports(NS_CHATSTATES):
|
||||||
# no info about peer
|
|
||||||
# send active to discover chat state capabilities
|
|
||||||
# this is here (and not in send_chatstate)
|
|
||||||
# because we want it sent with REAL message
|
|
||||||
# (not standlone) eg. one that has body
|
|
||||||
|
|
||||||
if contact.our_chatstate:
|
|
||||||
# We already asked for xep 85, don't ask it twice
|
|
||||||
composing_xep = 'asked_once'
|
|
||||||
|
|
||||||
chatstate_to_send = 'active'
|
|
||||||
contact.our_chatstate = 'ask' # pseudo state
|
|
||||||
# if peer supports jep85 and we are not 'ask', send 'active'
|
|
||||||
# NOTE: first active and 'ask' is set in gajim.py
|
|
||||||
elif composing_xep is not False:
|
|
||||||
# send active chatstate on every message (as XEP says)
|
# send active chatstate on every message (as XEP says)
|
||||||
chatstate_to_send = 'active'
|
chatstate_to_send = 'active'
|
||||||
contact.our_chatstate = 'active'
|
contact.our_chatstate = 'active'
|
||||||
|
@ -2275,10 +2249,9 @@ class ChatControl(ChatControlBase):
|
||||||
xep0184_id=xep0184_id, xhtml=xhtml, displaymarking=displaymarking)
|
xep0184_id=xep0184_id, xhtml=xhtml, displaymarking=displaymarking)
|
||||||
|
|
||||||
ChatControlBase.send_message(self, message, keyID, type_='chat',
|
ChatControlBase.send_message(self, message, keyID, type_='chat',
|
||||||
chatstate=chatstate_to_send, composing_xep=composing_xep,
|
chatstate=chatstate_to_send, xhtml=xhtml, callback=_on_sent,
|
||||||
xhtml=xhtml, callback=_on_sent,
|
callback_args=[contact, message, encrypted, xhtml,
|
||||||
callback_args=[contact, message, encrypted, xhtml, self.get_seclabel()],
|
self.get_seclabel()], process_commands=process_commands)
|
||||||
process_commands=process_commands)
|
|
||||||
|
|
||||||
|
|
||||||
def on_message_sent(self, account_and_message):
|
def on_message_sent(self, account_and_message):
|
||||||
|
@ -2598,7 +2571,7 @@ class ChatControl(ChatControlBase):
|
||||||
if contact.show == 'offline':
|
if contact.show == 'offline':
|
||||||
return
|
return
|
||||||
|
|
||||||
if contact.composing_xep is False: # jid cannot do xep85 nor xep22
|
if not contact.supports(NS_CHATSTATES):
|
||||||
return
|
return
|
||||||
|
|
||||||
# if the new state we wanna send (state) equals
|
# if the new state we wanna send (state) equals
|
||||||
|
@ -2606,28 +2579,7 @@ class ChatControl(ChatControlBase):
|
||||||
if contact.our_chatstate == state:
|
if contact.our_chatstate == state:
|
||||||
return
|
return
|
||||||
|
|
||||||
if contact.composing_xep is None:
|
# if wel're inactive prevent composing (XEP violation)
|
||||||
# we don't know anything about jid, so return
|
|
||||||
# NOTE:
|
|
||||||
# send 'active', set current state to 'ask' and return is done
|
|
||||||
# in self.send_message() because we need REAL message (with <body>)
|
|
||||||
# for that procedure so return to make sure we send only once
|
|
||||||
# 'active' until we know peer supports jep85
|
|
||||||
return
|
|
||||||
|
|
||||||
if contact.our_chatstate == 'ask':
|
|
||||||
return
|
|
||||||
|
|
||||||
# in JEP22, when we already sent stop composing
|
|
||||||
# notification on paused, don't resend it
|
|
||||||
if contact.composing_xep == 'XEP-0022' and \
|
|
||||||
contact.our_chatstate in ('paused', 'active', 'inactive') and \
|
|
||||||
state is not 'composing': # not composing == in (active, inactive, gone)
|
|
||||||
contact.our_chatstate = 'active'
|
|
||||||
self.reset_kbd_mouse_timeout_vars()
|
|
||||||
return
|
|
||||||
|
|
||||||
# if we're inactive prevent composing (JEP violation)
|
|
||||||
if contact.our_chatstate == 'inactive' and state == 'composing':
|
if contact.our_chatstate == 'inactive' and state == 'composing':
|
||||||
# go active before
|
# go active before
|
||||||
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
||||||
|
@ -2638,11 +2590,10 @@ class ChatControl(ChatControlBase):
|
||||||
|
|
||||||
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
gajim.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
||||||
account=self.account, jid=self.contact.jid, chatstate=state,
|
account=self.account, jid=self.contact.jid, chatstate=state,
|
||||||
msg_id=contact.msg_id, composing_xep=contact.composing_xep,
|
msg_id=contact.msg_id, control=self))
|
||||||
control=self))
|
|
||||||
|
|
||||||
contact.our_chatstate = state
|
contact.our_chatstate = state
|
||||||
if contact.our_chatstate == 'active':
|
if state == 'active':
|
||||||
self.reset_kbd_mouse_timeout_vars()
|
self.reset_kbd_mouse_timeout_vars()
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
|
@ -248,9 +248,9 @@ class CommonConnection:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
|
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
|
||||||
chatstate=None, msg_id=None, composing_xep=None, resource=None,
|
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||||
user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
|
session=None, forward_from=None, form_node=None, label=None,
|
||||||
label=None, original_message=None, delayed=None, callback=None):
|
original_message=None, delayed=None, callback=None):
|
||||||
if not self.connection or self.connected < 2:
|
if not self.connection or self.connected < 2:
|
||||||
return 1
|
return 1
|
||||||
try:
|
try:
|
||||||
|
@ -299,35 +299,34 @@ class CommonConnection:
|
||||||
self._message_encrypted_cb(output, type_, msg,
|
self._message_encrypted_cb(output, type_, msg,
|
||||||
msgtxt, original_message, fjid, resource,
|
msgtxt, original_message, fjid, resource,
|
||||||
jid, xhtml, subject, chatstate, msg_id,
|
jid, xhtml, subject, chatstate, msg_id,
|
||||||
composing_xep, label, forward_from, delayed,
|
label, forward_from, delayed, session,
|
||||||
session, form_node, user_nick, keyID,
|
form_node, user_nick, keyID, callback)
|
||||||
callback)
|
|
||||||
gajim.nec.push_incoming_event(GPGTrustKeyEvent(None,
|
gajim.nec.push_incoming_event(GPGTrustKeyEvent(None,
|
||||||
conn=self, callback=_on_always_trust))
|
conn=self, callback=_on_always_trust))
|
||||||
else:
|
else:
|
||||||
self._message_encrypted_cb(output, type_, msg, msgtxt,
|
self._message_encrypted_cb(output, type_, msg, msgtxt,
|
||||||
original_message, fjid, resource, jid, xhtml,
|
original_message, fjid, resource, jid, xhtml,
|
||||||
subject, chatstate, msg_id, composing_xep, label,
|
subject, chatstate, msg_id, label, forward_from,
|
||||||
forward_from, delayed, session, form_node,
|
delayed, session, form_node, user_nick, keyID,
|
||||||
user_nick, keyID, callback)
|
callback)
|
||||||
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
|
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
|
||||||
_on_encrypted, [])
|
_on_encrypted, [])
|
||||||
return
|
return
|
||||||
|
|
||||||
self._message_encrypted_cb(('', error), type_, msg, msgtxt,
|
self._message_encrypted_cb(('', error), type_, msg, msgtxt,
|
||||||
original_message, fjid, resource, jid, xhtml, subject,
|
original_message, fjid, resource, jid, xhtml, subject,
|
||||||
chatstate, msg_id, composing_xep, label, forward_from, delayed,
|
chatstate, msg_id, label, forward_from, delayed, session,
|
||||||
session, form_node, user_nick, keyID, callback)
|
form_node, user_nick, keyID, callback)
|
||||||
|
|
||||||
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
|
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
|
||||||
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
||||||
composing_xep, label, forward_from, delayed, session, form_node,
|
label, forward_from, delayed, session, form_node, user_nick,
|
||||||
user_nick, callback)
|
callback)
|
||||||
|
|
||||||
def _message_encrypted_cb(self, output, type_, msg, msgtxt,
|
def _message_encrypted_cb(self, output, type_, msg, msgtxt,
|
||||||
original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
|
original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
|
||||||
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
|
label, forward_from, delayed, session, form_node, user_nick, keyID,
|
||||||
keyID, callback):
|
callback):
|
||||||
msgenc, error = output
|
msgenc, error = output
|
||||||
|
|
||||||
if msgenc and not error:
|
if msgenc and not error:
|
||||||
|
@ -339,8 +338,8 @@ class CommonConnection:
|
||||||
' (' + msgtxt + ')'
|
' (' + msgtxt + ')'
|
||||||
self._on_continue_message(type_, msg, msgtxt, original_message,
|
self._on_continue_message(type_, msg, msgtxt, original_message,
|
||||||
fjid, resource, jid, xhtml, subject, msgenc, keyID,
|
fjid, resource, jid, xhtml, subject, msgenc, keyID,
|
||||||
chatstate, msg_id, composing_xep, label, forward_from, delayed,
|
chatstate, msg_id, label, forward_from, delayed, session,
|
||||||
session, form_node, user_nick, callback)
|
form_node, user_nick, callback)
|
||||||
return
|
return
|
||||||
# Encryption failed, do not send message
|
# Encryption failed, do not send message
|
||||||
tim = localtime()
|
tim = localtime()
|
||||||
|
@ -349,8 +348,7 @@ class CommonConnection:
|
||||||
|
|
||||||
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
|
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
|
||||||
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
||||||
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
|
label, forward_from, delayed, session, form_node, user_nick, callback):
|
||||||
callback):
|
|
||||||
if type_ == 'chat':
|
if type_ == 'chat':
|
||||||
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
|
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
|
||||||
xhtml=xhtml)
|
xhtml=xhtml)
|
||||||
|
@ -386,27 +384,17 @@ class CommonConnection:
|
||||||
contact = gajim.contacts.get_contact_with_highest_priority(self.name,
|
contact = gajim.contacts.get_contact_with_highest_priority(self.name,
|
||||||
jid)
|
jid)
|
||||||
|
|
||||||
# chatstates - if peer supports xep85 or xep22, send chatstates
|
# chatstates - if peer supports xep85, send chatstates
|
||||||
# please note that the only valid tag inside a message containing a <body>
|
# please note that the only valid tag inside a message containing a
|
||||||
# tag is the active event
|
# <body> tag is the active event
|
||||||
if chatstate is not None and contact:
|
if chatstate and contact and contact.supports(NS_CHATSTATES):
|
||||||
if ((composing_xep == 'XEP-0085' or not composing_xep) \
|
msg_iq.setTag(chatstate, namespace=NS_CHATSTATES)
|
||||||
and composing_xep != 'asked_once') or \
|
|
||||||
contact.supports(common.xmpp.NS_CHATSTATES):
|
|
||||||
# XEP-0085
|
|
||||||
msg_iq.setTag(chatstate, namespace=common.xmpp.NS_CHATSTATES)
|
|
||||||
if composing_xep in ('XEP-0022', 'asked_once') or \
|
|
||||||
not composing_xep:
|
|
||||||
# XEP-0022
|
|
||||||
chatstate_node = msg_iq.setTag('x', namespace=common.xmpp.NS_EVENT)
|
|
||||||
if chatstate is 'composing' or msgtxt:
|
|
||||||
chatstate_node.addChild(name='composing')
|
|
||||||
|
|
||||||
if forward_from:
|
if forward_from:
|
||||||
addresses = msg_iq.addChild('addresses',
|
addresses = msg_iq.addChild('addresses',
|
||||||
namespace=common.xmpp.NS_ADDRESS)
|
namespace=common.xmpp.NS_ADDRESS)
|
||||||
addresses.addChild('address', attrs = {'type': 'ofrom',
|
addresses.addChild('address', attrs = {'type': 'ofrom',
|
||||||
'jid': forward_from})
|
'jid': forward_from})
|
||||||
|
|
||||||
# XEP-0203
|
# XEP-0203
|
||||||
if delayed:
|
if delayed:
|
||||||
|
@ -433,7 +421,7 @@ class CommonConnection:
|
||||||
|
|
||||||
if callback:
|
if callback:
|
||||||
callback(jid, msg, keyID, forward_from, session, original_message,
|
callback(jid, msg, keyID, forward_from, session, original_message,
|
||||||
subject, type_, msg_iq, xhtml)
|
subject, type_, msg_iq, xhtml)
|
||||||
|
|
||||||
def log_message(self, jid, msg, forward_from, session, original_message,
|
def log_message(self, jid, msg, forward_from, session, original_message,
|
||||||
subject, type_, xhtml=None):
|
subject, type_, xhtml=None):
|
||||||
|
@ -1791,10 +1779,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.connection.send(msg_iq)
|
self.connection.send(msg_iq)
|
||||||
|
|
||||||
def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
|
def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
|
||||||
chatstate=None, msg_id=None, composing_xep=None, resource=None,
|
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||||
user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
|
label=None, session=None, forward_from=None, form_node=None,
|
||||||
form_node=None, original_message=None, delayed=None, callback=None,
|
original_message=None, delayed=None, callback=None, callback_args=[],
|
||||||
callback_args=[], now=False):
|
now=False):
|
||||||
|
|
||||||
def cb(jid, msg, keyID, forward_from, session, original_message,
|
def cb(jid, msg, keyID, forward_from, session, original_message,
|
||||||
subject, type_, msg_iq, xhtml):
|
subject, type_, msg_iq, xhtml):
|
||||||
|
@ -1809,9 +1797,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
subject, type_, xhtml)
|
subject, type_, xhtml)
|
||||||
|
|
||||||
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
|
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
|
||||||
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
|
chatstate=chatstate, msg_id=msg_id, resource=resource,
|
||||||
resource=resource, user_nick=user_nick, xhtml=xhtml, label=label,
|
user_nick=user_nick, xhtml=xhtml, label=label, session=session,
|
||||||
session=session, forward_from=forward_from, form_node=form_node,
|
forward_from=forward_from, form_node=form_node,
|
||||||
original_message=original_message, delayed=delayed, callback=cb)
|
original_message=original_message, delayed=delayed, callback=cb)
|
||||||
|
|
||||||
def _nec_message_outgoing(self, obj):
|
def _nec_message_outgoing(self, obj):
|
||||||
|
@ -1834,9 +1822,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
|
|
||||||
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
|
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
|
||||||
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
|
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
|
||||||
composing_xep=obj.composing_xep, resource=obj.resource,
|
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
|
||||||
user_nick=obj.user_nick, xhtml=obj.xhtml, label=obj.label,
|
label=obj.label, session=obj.session, forward_from=obj.forward_from,
|
||||||
session=obj.session, forward_from=obj.forward_from,
|
|
||||||
form_node=obj.form_node, original_message=obj.original_message,
|
form_node=obj.form_node, original_message=obj.original_message,
|
||||||
delayed=obj.delayed, callback=cb)
|
delayed=obj.delayed, callback=cb)
|
||||||
|
|
||||||
|
|
|
@ -907,8 +907,7 @@ class ConnectionHandlersBase:
|
||||||
# reset chatstate if needed:
|
# reset chatstate if needed:
|
||||||
# (when contact signs out or has errors)
|
# (when contact signs out or has errors)
|
||||||
if obj.show in ('offline', 'error'):
|
if obj.show in ('offline', 'error'):
|
||||||
obj.contact.our_chatstate = obj.contact.chatstate = \
|
obj.contact.our_chatstate = obj.contact.chatstate = None
|
||||||
obj.contact.composing_xep = None
|
|
||||||
|
|
||||||
# TODO: This causes problems when another
|
# TODO: This causes problems when another
|
||||||
# resource signs off!
|
# resource signs off!
|
||||||
|
|
|
@ -35,6 +35,7 @@ from common import exceptions
|
||||||
from common.zeroconf import zeroconf
|
from common.zeroconf import zeroconf
|
||||||
from common.logger import LOG_DB_PATH
|
from common.logger import LOG_DB_PATH
|
||||||
from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
|
from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
|
||||||
|
from common.xmpp.protocol import NS_CHATSTATES
|
||||||
|
|
||||||
import gtkgui_helpers
|
import gtkgui_helpers
|
||||||
|
|
||||||
|
@ -74,28 +75,16 @@ class HelperEvent:
|
||||||
Extract chatstate from a <message/> stanza
|
Extract chatstate from a <message/> stanza
|
||||||
Requires self.stanza and self.msgtxt
|
Requires self.stanza and self.msgtxt
|
||||||
"""
|
"""
|
||||||
self.composing_xep = None
|
|
||||||
self.chatstate = None
|
self.chatstate = None
|
||||||
|
|
||||||
# chatstates - look for chatstate tags in a message if not delayed
|
# chatstates - look for chatstate tags in a message if not delayed
|
||||||
delayed = self.stanza.getTag('x', namespace=xmpp.NS_DELAY) is not None
|
delayed = self.stanza.getTag('x', namespace=xmpp.NS_DELAY) is not None
|
||||||
if not delayed:
|
if not delayed:
|
||||||
self.composing_xep = False
|
|
||||||
children = self.stanza.getChildren()
|
children = self.stanza.getChildren()
|
||||||
for child in children:
|
for child in children:
|
||||||
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
|
if child.getNamespace() == NS_CHATSTATES:
|
||||||
self.chatstate = child.getName()
|
self.chatstate = child.getName()
|
||||||
self.composing_xep = 'XEP-0085'
|
|
||||||
break
|
break
|
||||||
# No XEP-0085 support, fallback to XEP-0022
|
|
||||||
if not self.chatstate:
|
|
||||||
chatstate_child = self.stanza.getTag('x',
|
|
||||||
namespace=xmpp.NS_EVENT)
|
|
||||||
if chatstate_child:
|
|
||||||
self.chatstate = 'active'
|
|
||||||
self.composing_xep = 'XEP-0022'
|
|
||||||
if not self.msgtxt and chatstate_child.getTag('composing'):
|
|
||||||
self.chatstate = 'composing'
|
|
||||||
|
|
||||||
class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
|
class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'http-auth-received'
|
name = 'http-auth-received'
|
||||||
|
@ -1170,7 +1159,6 @@ class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
self.jid = self.msg_obj.jid
|
self.jid = self.msg_obj.jid
|
||||||
self.fjid = self.msg_obj.fjid
|
self.fjid = self.msg_obj.fjid
|
||||||
self.resource = self.msg_obj.resource
|
self.resource = self.msg_obj.resource
|
||||||
self.composing_xep = self.msg_obj.composing_xep
|
|
||||||
self.chatstate = self.msg_obj.chatstate
|
self.chatstate = self.msg_obj.chatstate
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -2177,7 +2165,6 @@ class MessageOutgoingEvent(nec.NetworkIncomingEvent):
|
||||||
self.subject = ''
|
self.subject = ''
|
||||||
self.chatstate = None
|
self.chatstate = None
|
||||||
self.msg_id = None
|
self.msg_id = None
|
||||||
self.composing_xep = None
|
|
||||||
self.resource = None
|
self.resource = None
|
||||||
self.user_nick = None
|
self.user_nick = None
|
||||||
self.xhtml = None
|
self.xhtml = None
|
||||||
|
|
|
@ -46,7 +46,7 @@ class XMPPEntity(object):
|
||||||
class CommonContact(XMPPEntity):
|
class CommonContact(XMPPEntity):
|
||||||
|
|
||||||
def __init__(self, jid, account, resource, show, status, name,
|
def __init__(self, jid, account, resource, show, status, name,
|
||||||
our_chatstate, composing_xep, chatstate, client_caps=None):
|
our_chatstate, chatstate, client_caps=None):
|
||||||
|
|
||||||
XMPPEntity.__init__(self, jid, account, resource)
|
XMPPEntity.__init__(self, jid, account, resource)
|
||||||
|
|
||||||
|
@ -57,17 +57,8 @@ class CommonContact(XMPPEntity):
|
||||||
self.client_caps = client_caps or caps_cache.NullClientCaps()
|
self.client_caps = client_caps or caps_cache.NullClientCaps()
|
||||||
|
|
||||||
# please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
|
# please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
|
||||||
# we keep track of xep85 support with the peer by three extra states:
|
|
||||||
# None, False and 'ask'
|
|
||||||
# None if no info about peer
|
|
||||||
# False if peer does not support xep85
|
|
||||||
# 'ask' if we sent the first 'active' chatstate and are waiting for reply
|
|
||||||
# this holds what WE SEND to contact (our current chatstate)
|
# this holds what WE SEND to contact (our current chatstate)
|
||||||
self.our_chatstate = our_chatstate
|
self.our_chatstate = our_chatstate
|
||||||
# tell which XEP we're using for composing state
|
|
||||||
# None = have to ask, XEP-0022 = use this xep,
|
|
||||||
# XEP-0085 = use this xep, False = no composing support
|
|
||||||
self.composing_xep = composing_xep
|
|
||||||
# this is contact's chatstate
|
# this is contact's chatstate
|
||||||
self.chatstate = chatstate
|
self.chatstate = chatstate
|
||||||
|
|
||||||
|
@ -97,12 +88,12 @@ class Contact(CommonContact):
|
||||||
Information concerning a contact
|
Information concerning a contact
|
||||||
"""
|
"""
|
||||||
def __init__(self, jid, account, name='', groups=[], show='', status='',
|
def __init__(self, jid, account, name='', groups=[], show='', status='',
|
||||||
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
||||||
our_chatstate=None, chatstate=None, last_status_time=None, msg_id=
|
our_chatstate=None, chatstate=None, last_status_time=None, msg_id=None,
|
||||||
None, composing_xep=None, last_activity_time=None):
|
last_activity_time=None):
|
||||||
|
|
||||||
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
||||||
our_chatstate, composing_xep, chatstate, client_caps=client_caps)
|
our_chatstate, chatstate, client_caps=client_caps)
|
||||||
|
|
||||||
self.contact_name = '' # nick choosen by contact
|
self.contact_name = '' # nick choosen by contact
|
||||||
self.groups = [i for i in set(groups)] # filter duplicate values
|
self.groups = [i for i in set(groups)] # filter duplicate values
|
||||||
|
@ -184,11 +175,11 @@ class GC_Contact(CommonContact):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, room_jid, account, name='', show='', status='', role='',
|
def __init__(self, room_jid, account, name='', show='', status='', role='',
|
||||||
affiliation='', jid='', resource='', our_chatstate=None,
|
affiliation='', jid='', resource='', our_chatstate=None,
|
||||||
composing_xep=None, chatstate=None):
|
chatstate=None):
|
||||||
|
|
||||||
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
CommonContact.__init__(self, jid, account, resource, show, status, name,
|
||||||
our_chatstate, composing_xep, chatstate)
|
our_chatstate, chatstate)
|
||||||
|
|
||||||
self.room_jid = room_jid
|
self.room_jid = room_jid
|
||||||
self.role = role
|
self.role = role
|
||||||
|
@ -205,8 +196,8 @@ class GC_Contact(CommonContact):
|
||||||
Create a Contact instance from this GC_Contact instance
|
Create a Contact instance from this GC_Contact instance
|
||||||
"""
|
"""
|
||||||
return Contact(jid=self.get_full_jid(), account=self.account,
|
return Contact(jid=self.get_full_jid(), account=self.account,
|
||||||
name=self.name, groups=[], show=self.show, status=self.status,
|
name=self.name, groups=[], show=self.show, status=self.status,
|
||||||
sub='none', client_caps=self.client_caps)
|
sub='none', client_caps=self.client_caps)
|
||||||
|
|
||||||
|
|
||||||
class LegacyContactsAPI:
|
class LegacyContactsAPI:
|
||||||
|
@ -249,15 +240,15 @@ class LegacyContactsAPI:
|
||||||
def create_contact(self, jid, account, name='', groups=[], show='',
|
def create_contact(self, jid, account, name='', groups=[], show='',
|
||||||
status='', sub='', ask='', resource='', priority=0, keyID='',
|
status='', sub='', ask='', resource='', priority=0, keyID='',
|
||||||
client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None,
|
client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None,
|
||||||
composing_xep=None, last_activity_time=None):
|
last_activity_time=None):
|
||||||
# Use Account object if available
|
# Use Account object if available
|
||||||
account = self._accounts.get(account, account)
|
account = self._accounts.get(account, account)
|
||||||
return Contact(jid=jid, account=account, name=name, groups=groups,
|
return Contact(jid=jid, account=account, name=name, groups=groups,
|
||||||
show=show, status=status, sub=sub, ask=ask, resource=resource,
|
show=show, status=status, sub=sub, ask=ask, resource=resource,
|
||||||
priority=priority, keyID=keyID, client_caps=client_caps,
|
priority=priority, keyID=keyID, client_caps=client_caps,
|
||||||
our_chatstate=our_chatstate, chatstate=chatstate,
|
our_chatstate=our_chatstate, chatstate=chatstate,
|
||||||
last_status_time=last_status_time, composing_xep=composing_xep,
|
last_status_time=last_status_time,
|
||||||
last_activity_time=last_activity_time)
|
last_activity_time=last_activity_time)
|
||||||
|
|
||||||
def create_self_contact(self, jid, account, resource, show, status, priority,
|
def create_self_contact(self, jid, account, resource, show, status, priority,
|
||||||
name='', keyID=''):
|
name='', keyID=''):
|
||||||
|
@ -266,27 +257,28 @@ class LegacyContactsAPI:
|
||||||
account = self._accounts.get(account, account) # Use Account object if available
|
account = self._accounts.get(account, account) # Use Account object if available
|
||||||
self_contact = self.create_contact(jid=jid, account=account,
|
self_contact = self.create_contact(jid=jid, account=account,
|
||||||
name=nick, groups=['self_contact'], show=show, status=status,
|
name=nick, groups=['self_contact'], show=show, status=status,
|
||||||
sub='both', ask='none', priority=priority, keyID=keyID,
|
sub='both', ask='none', priority=priority, keyID=keyID,
|
||||||
resource=resource)
|
resource=resource)
|
||||||
self_contact.pep = conn.pep
|
self_contact.pep = conn.pep
|
||||||
return self_contact
|
return self_contact
|
||||||
|
|
||||||
def create_not_in_roster_contact(self, jid, account, resource='', name='', keyID=''):
|
def create_not_in_roster_contact(self, jid, account, resource='', name='',
|
||||||
account = self._accounts.get(account, account) # Use Account object if available
|
keyID=''):
|
||||||
|
# Use Account object if available
|
||||||
|
account = self._accounts.get(account, account)
|
||||||
return self.create_contact(jid=jid, account=account, resource=resource,
|
return self.create_contact(jid=jid, account=account, resource=resource,
|
||||||
name=name, groups=[_('Not in Roster')], show='not in roster',
|
name=name, groups=[_('Not in Roster')], show='not in roster',
|
||||||
status='', sub='none', keyID=keyID)
|
status='', sub='none', keyID=keyID)
|
||||||
|
|
||||||
def copy_contact(self, contact):
|
def copy_contact(self, contact):
|
||||||
return self.create_contact(contact.jid, contact.account,
|
return self.create_contact(contact.jid, contact.account,
|
||||||
name=contact.name, groups=contact.groups, show=contact.show,
|
name=contact.name, groups=contact.groups, show=contact.show,
|
||||||
status=contact.status, sub=contact.sub, ask=contact.ask,
|
status=contact.status, sub=contact.sub, ask=contact.ask,
|
||||||
resource=contact.resource, priority=contact.priority,
|
resource=contact.resource, priority=contact.priority,
|
||||||
keyID=contact.keyID, client_caps=contact.client_caps,
|
keyID=contact.keyID, client_caps=contact.client_caps,
|
||||||
our_chatstate=contact.our_chatstate, chatstate=contact.chatstate,
|
our_chatstate=contact.our_chatstate, chatstate=contact.chatstate,
|
||||||
last_status_time=contact.last_status_time,
|
last_status_time=contact.last_status_time,
|
||||||
composing_xep=contact.composing_xep,
|
last_activity_time=contact.last_activity_time)
|
||||||
last_activity_time=contact.last_activity_time)
|
|
||||||
|
|
||||||
def add_contact(self, account, contact):
|
def add_contact(self, account, contact):
|
||||||
if account not in self._accounts:
|
if account not in self._accounts:
|
||||||
|
|
|
@ -333,10 +333,10 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
msg=_('Please check if avahi-daemon is running.')))
|
msg=_('Please check if avahi-daemon is running.')))
|
||||||
|
|
||||||
def send_message(self, jid, msg, keyID, type_='chat', subject='',
|
def send_message(self, jid, msg, keyID, type_='chat', subject='',
|
||||||
chatstate=None, msg_id=None, composing_xep=None, resource=None,
|
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||||
user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
|
label=None, session=None, forward_from=None, form_node=None,
|
||||||
form_node=None, original_message=None, delayed=None, callback=None,
|
original_message=None, delayed=None, callback=None, callback_args=[],
|
||||||
callback_args=[], now=True):
|
now=True):
|
||||||
|
|
||||||
def on_send_ok(msg_id):
|
def on_send_ok(msg_id):
|
||||||
gajim.nec.push_incoming_event(MessageSentEvent(None, conn=self,
|
gajim.nec.push_incoming_event(MessageSentEvent(None, conn=self,
|
||||||
|
@ -366,8 +366,8 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
msg=None, time_=None, session=session))
|
msg=None, time_=None, session=session))
|
||||||
|
|
||||||
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
|
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
|
||||||
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
|
chatstate=chatstate, msg_id=msg_id, resource=resource,
|
||||||
resource=resource, user_nick=user_nick, xhtml=xhtml, session=session,
|
user_nick=user_nick, xhtml=xhtml, session=session,
|
||||||
forward_from=forward_from, form_node=form_node,
|
forward_from=forward_from, form_node=form_node,
|
||||||
original_message=original_message, delayed=delayed, callback=cb)
|
original_message=original_message, delayed=delayed, callback=cb)
|
||||||
|
|
||||||
|
@ -406,9 +406,8 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
|
|
||||||
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
|
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
|
||||||
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
|
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
|
||||||
composing_xep=obj.composing_xep, resource=obj.resource,
|
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
|
||||||
user_nick=obj.user_nick, xhtml=obj.xhtml, label=obj.label,
|
label=obj.label, session=obj.session, forward_from=obj.forward_from,
|
||||||
session=obj.session, forward_from=obj.forward_from,
|
|
||||||
form_node=obj.form_node, original_message=obj.original_message,
|
form_node=obj.form_node, original_message=obj.original_message,
|
||||||
delayed=obj.delayed, callback=cb)
|
delayed=obj.delayed, callback=cb)
|
||||||
|
|
||||||
|
|
|
@ -368,11 +368,6 @@ class Interface:
|
||||||
jids = obj.fjid.split('/', 1)
|
jids = obj.fjid.split('/', 1)
|
||||||
jid = jids[0]
|
jid = jids[0]
|
||||||
|
|
||||||
if obj.error_code == '503':
|
|
||||||
# If we get server-not-found error, stop sending chatstates
|
|
||||||
for contact in gajim.contacts.get_contacts(account, jid):
|
|
||||||
contact.composing_xep = False
|
|
||||||
|
|
||||||
session = obj.session
|
session = obj.session
|
||||||
|
|
||||||
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||||
|
|
|
@ -138,8 +138,7 @@ class Remote:
|
||||||
|
|
||||||
def on_chatstate_received(self, obj):
|
def on_chatstate_received(self, obj):
|
||||||
self.raise_signal('ChatState', (obj.conn.name, [
|
self.raise_signal('ChatState', (obj.conn.name, [
|
||||||
obj.jid, obj.fjid, obj.stanza, obj.resource, obj.composing_xep,
|
obj.jid, obj.fjid, obj.stanza, obj.resource, obj.chatstate]))
|
||||||
obj.chatstate]))
|
|
||||||
|
|
||||||
def on_message_sent(self, obj):
|
def on_message_sent(self, obj):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -116,8 +116,6 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
||||||
contact = gajim.contacts.get_contact(self.conn.name, obj.jid,
|
contact = gajim.contacts.get_contact(self.conn.name, obj.jid,
|
||||||
obj.resource)
|
obj.resource)
|
||||||
if contact:
|
if contact:
|
||||||
if contact.composing_xep != 'XEP-0085': # We cache xep85 support
|
|
||||||
contact.composing_xep = obj.composing_xep
|
|
||||||
if self.control and self.control.type_id == \
|
if self.control and self.control.type_id == \
|
||||||
message_control.TYPE_CHAT:
|
message_control.TYPE_CHAT:
|
||||||
if obj.chatstate is not None:
|
if obj.chatstate is not None:
|
||||||
|
@ -169,7 +167,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
||||||
gajim.interface.remote_ctrl.raise_signal('NewMessage', (
|
gajim.interface.remote_ctrl.raise_signal('NewMessage', (
|
||||||
self.conn.name, [obj.fjid, obj.msgtxt, obj.timestamp,
|
self.conn.name, [obj.fjid, obj.msgtxt, obj.timestamp,
|
||||||
obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id,
|
obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id,
|
||||||
obj.composing_xep, obj.user_nick, obj.xhtml, obj.form_node]))
|
obj.user_nick, obj.xhtml, obj.form_node]))
|
||||||
|
|
||||||
def roster_message2(self, obj):
|
def roster_message2(self, obj):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -15,8 +15,8 @@ class TestCommonContact(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.contact = CommonContact(jid='', account="", resource='', show='',
|
self.contact = CommonContact(jid='', account="", resource='', show='',
|
||||||
status='', name='', our_chatstate=None, composing_xep=None,
|
status='', name='', our_chatstate=None, chatstate=None,
|
||||||
chatstate=None, client_caps=None)
|
client_caps=None)
|
||||||
|
|
||||||
def test_default_client_supports(self):
|
def test_default_client_supports(self):
|
||||||
'''
|
'''
|
||||||
|
@ -44,7 +44,7 @@ class TestContact(TestCommonContact):
|
||||||
domain model by smoke testing that no attribute values are lost'''
|
domain model by smoke testing that no attribute values are lost'''
|
||||||
|
|
||||||
attributes = ["jid", "resource", "show", "status", "name", "our_chatstate",
|
attributes = ["jid", "resource", "show", "status", "name", "our_chatstate",
|
||||||
"composing_xep", "chatstate", "client_caps", "priority", "sub"]
|
"chatstate", "client_caps", "priority", "sub"]
|
||||||
for attr in attributes:
|
for attr in attributes:
|
||||||
self.assertTrue(hasattr(self.contact, attr), msg="expected: " + attr)
|
self.assertTrue(hasattr(self.contact, attr), msg="expected: " + attr)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class TestGC_Contact(TestCommonContact):
|
||||||
domain model by asserting no attributes have been lost'''
|
domain model by asserting no attributes have been lost'''
|
||||||
|
|
||||||
attributes = ["jid", "resource", "show", "status", "name", "our_chatstate",
|
attributes = ["jid", "resource", "show", "status", "name", "our_chatstate",
|
||||||
"composing_xep", "chatstate", "client_caps", "role", "room_jid"]
|
"chatstate", "client_caps", "role", "room_jid"]
|
||||||
for attr in attributes:
|
for attr in attributes:
|
||||||
self.assertTrue(hasattr(self.contact, attr), msg="expected: " + attr)
|
self.assertTrue(hasattr(self.contact, attr), msg="expected: " + attr)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue