Merge branch 'master' into 'master'

Sending Message Refactoring

See merge request !80
This commit is contained in:
Philipp Hörist 2017-04-08 18:38:55 +02:00
commit 8a789763a2
3 changed files with 187 additions and 233 deletions

View file

@ -263,232 +263,202 @@ class CommonConnection:
""" """
raise NotImplementedError raise NotImplementedError
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='', def _prepare_message(self, obj):
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
session=None, forward_from=None, form_node=None, label=None,
original_message=None, delayed=None, attention=False, correction_msg=None,
callback=None):
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return 1 return 1
if isinstance(jid, list): if isinstance(obj.jid, list):
new_list = [] for jid in obj.jid:
for j in jid:
try: try:
new_list.append(self.check_jid(j)) self.check_jid(jid)
except helpers.InvalidFormat: except helpers.InvalidFormat:
gajim.nec.push_incoming_event(InformationEvent(None, gajim.nec.push_incoming_event(InformationEvent(None,
conn=self, level='error', pri_txt=_('Invalid JID'), conn=self, level='error', pri_txt=_('Invalid JID'),
sec_txt=_('It is not possible to send a message ' sec_txt=_('It is not possible to send a message '
'to %s, this JID is not valid.') % j)) 'to %s, this JID is not valid.') % jid))
return return
fjid = new_list
else: else:
try: try:
jid = self.check_jid(jid) self.check_jid(obj.jid)
except helpers.InvalidFormat: except helpers.InvalidFormat:
gajim.nec.push_incoming_event(InformationEvent(None, conn=self, gajim.nec.push_incoming_event(InformationEvent(None, conn=self,
level='error', pri_txt=_('Invalid JID'), sec_txt=_( level='error', pri_txt=_('Invalid JID'), sec_txt=_(
'It is not possible to send a message to %s, this JID is not ' 'It is not possible to send a message to %s, this JID is not '
'valid.') % jid)) 'valid.') % obj.jid))
return return
fjid = jid
if resource:
fjid += '/' + resource
if session: if obj.message and not obj.xhtml and gajim.config.get(
fjid = session.get_to()
if msg and not xhtml and gajim.config.get(
'rst_formatting_outgoing_messages'): 'rst_formatting_outgoing_messages'):
from common.rst_xhtml_generator import create_xhtml from common.rst_xhtml_generator import create_xhtml
xhtml = create_xhtml(msg) obj.xhtml = create_xhtml(obj.message)
if not msg and chatstate is None and form_node is None: if not obj.message and obj.chatstate is None and obj.form_node is None:
return return
msgtxt = msg if obj.keyID and self.USE_GPG:
msgenc = '' self._encrypt_message(obj)
return
if keyID and self.USE_GPG: self._build_message_stanza(obj)
xhtml = None
if keyID == 'UNKNOWN': def _encrypt_message(self, obj):
obj.xhtml = None
if obj.keyID == 'UNKNOWN':
error = _('Neither the remote presence is signed, nor a key was ' error = _('Neither the remote presence is signed, nor a key was '
'assigned.') 'assigned.')
elif keyID.endswith('MISMATCH'): elif obj.keyID.endswith('MISMATCH'):
error = _('The contact\'s key (%s) does not match the key assigned ' error = _('The contact\'s key (%s) does not match the key assigned '
'in Gajim.' % keyID[:8]) 'in Gajim.' % obj.keyID[:8])
else: else:
myKeyID = gajim.config.get_per('accounts', self.name, 'keyid') myKeyID = gajim.config.get_per('accounts', self.name, 'keyid')
def encrypt_thread(msg, keyID, always_trust=False): key_list = [obj.keyID, myKeyID]
# encrypt message. This function returns (msgenc, error)
return self.gpg.encrypt(msg, [keyID, myKeyID],
always_trust)
def _on_encrypted(output): def _on_encrypted(output):
msgenc, error = output msgenc, error = output
if error.startswith( 'NOT_TRUSTED'): if error.startswith('NOT_TRUSTED'):
def _on_always_trust(answer): def _on_always_trust(answer):
if answer: if answer:
gajim.thread_interface(encrypt_thread, [msg, keyID, gajim.thread_interface(
True], _on_encrypted, []) self.gpg.encrypt, [obj.message, key_list, True],
_on_encrypted, [])
else: else:
self._message_encrypted_cb(output, type_, msg, self._finished_encrypt(obj, msgenc=msgenc,
msgtxt, original_message, fjid, resource, error=error)
jid, xhtml, subject, chatstate, msg_id,
label, forward_from, delayed, session,
form_node, user_nick, keyID, attention,
correction_msg, callback)
gajim.nec.push_incoming_event(GPGTrustKeyEvent(None, gajim.nec.push_incoming_event(GPGTrustKeyEvent(None,
conn=self, keyID=error.split(' ')[-1], conn=self, keyID=error.split(' ')[-1],
callback=_on_always_trust)) callback=_on_always_trust))
else: else:
self._message_encrypted_cb(output, type_, msg, msgtxt, self._finished_encrypt(obj, msgenc=msgenc, error=error)
original_message, fjid, resource, jid, xhtml, gajim.thread_interface(
subject, chatstate, msg_id, label, forward_from, self.gpg.encrypt, [obj.message, key_list, False],
delayed, session, form_node, user_nick, keyID,
attention, correction_msg, callback)
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
_on_encrypted, []) _on_encrypted, [])
return return
self._finished_encrypt(obj, error=error)
self._message_encrypted_cb(('', error), type_, msg, msgtxt, def _finished_encrypt(self, obj, msgenc=None, error=None):
original_message, fjid, resource, jid, xhtml, subject, if error:
chatstate, msg_id, label, forward_from, delayed, session, gajim.nec.push_incoming_event(
form_node, user_nick, keyID, attention, correction_msg, MessageNotSentEvent(
callback) None, conn=self, jid=obj.jid, message=obj.message,
error=error, time_=time.time(), session=obj.session))
return return
self._build_message_stanza(obj, msgenc)
self._on_continue_message(type_, msg, msgtxt, original_message, fjid, def _build_message_stanza(self, obj, msgenc=None):
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id, if msgenc:
label, forward_from, delayed, session, form_node, user_nick,
attention, correction_msg, callback)
def _message_encrypted_cb(self, output, type_, msg, msgtxt,
original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
label, forward_from, delayed, session, form_node, user_nick, keyID,
attention, correction_msg, callback):
msgenc, error = output
if msgenc and not error:
msgtxt = '[This message is *encrypted* (See :XEP:`27`]' msgtxt = '[This message is *encrypted* (See :XEP:`27`]'
lang = os.getenv('LANG') lang = os.getenv('LANG')
if lang is not None and not lang.startswith('en'): if lang is not None and not lang.startswith('en'):
# we're not english: one in locale and one en # we're not english: one in locale and one en
msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \ msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \
' (' + msgtxt + ')' ' (' + msgtxt + ')'
self._on_continue_message(type_, msg, msgtxt, original_message, else:
fjid, resource, jid, xhtml, subject, msgenc, keyID, msgtxt = obj.message
chatstate, msg_id, label, forward_from, delayed, session,
form_node, user_nick, attention, correction_msg, callback)
return
# Encryption failed, do not send message
tim = time.localtime()
gajim.nec.push_incoming_event(MessageNotSentEvent(None, conn=self,
jid=jid, message=msgtxt, error=error, time_=tim, session=session))
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid, fjid = obj.get_full_jid()
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
label, forward_from, delayed, session, form_node, user_nick, attention,
correction_msg, callback):
if correction_msg: if obj.correction_msg:
id_ = correction_msg.getID() id_ = obj.correction_msg.getID()
if correction_msg.getTag('replace'): if obj.correction_msg.getTag('replace'):
correction_msg.delChild('replace') obj.correction_msg.delChild('replace')
correction_msg.setTag('replace', attrs={'id': id_}, obj.correction_msg.setTag('replace', attrs={'id': id_},
namespace=nbxmpp.NS_CORRECT) namespace=nbxmpp.NS_CORRECT)
id2 = self.connection.getAnID() id2 = self.connection.getAnID()
correction_msg.setID(id2) obj.correction_msg.setID(id2)
correction_msg.setBody(msgtxt) obj.correction_msg.setBody(msgtxt)
if xhtml: if obj.xhtml:
correction_msg.setXHTML(xhtml) obj.correction_msg.setXHTML(obj.xhtml)
if session:
session.last_send = time.time()
# XEP-0200
if session.enable_encryption:
correction_msg = session.encrypt_stanza(correction_msg)
if callback:
callback(jid, msg, keyID, forward_from, session, original_message,
subject, type_, correction_msg, xhtml)
return
if type_ == 'chat':
msg_iq = nbxmpp.Message(body=msgtxt, typ=type_,
xhtml=xhtml)
else:
if subject:
msg_iq = nbxmpp.Message(body=msgtxt, typ='normal',
subject=subject, xhtml=xhtml)
else:
msg_iq = nbxmpp.Message(body=msgtxt, typ='normal',
xhtml=xhtml)
if msg_id:
msg_iq.setID(msg_id)
if msgenc: if msgenc:
msg_iq.setTag(nbxmpp.NS_ENCRYPTED + ' x').setData(msgenc) encrypted_tag = obj.correction_msg.getTag(
'x', namespace=nbxmpp.NS_ENCRYPTED)
obj.correction_msg.delChild(encrypted_tag)
obj.correction_msg.setTag(
'x', namespace=nbxmpp.NS_ENCRYPTED).setData(msgenc)
if form_node: if obj.session:
msg_iq.addChild(node=form_node) obj.session.last_send = time.time()
if label:
msg_iq.addChild(node=label) # XEP-0200
if obj.session.enable_encryption:
obj.correction_msg = obj.session.encrypt_stanza(obj.correction_msg)
self._push_stanza_message_outgoing(obj, obj.correction_msg)
return
if obj.type_ == 'chat':
msg_iq = nbxmpp.Message(body=msgtxt, typ=obj.type_,
xhtml=obj.xhtml)
else:
if obj.subject:
msg_iq = nbxmpp.Message(body=msgtxt, typ='normal',
subject=obj.subject, xhtml=obj.xhtml)
else:
msg_iq = nbxmpp.Message(body=msgtxt, typ='normal',
xhtml=obj.xhtml)
if obj.msg_id:
msg_iq.setID(obj.msg_id)
if msgenc:
msg_iq.setTag('x', namespace=nbxmpp.NS_ENCRYPTED).setData(msgenc)
if obj.form_node:
msg_iq.addChild(node=obj.form_node)
if obj.label:
msg_iq.addChild(node=obj.label)
# XEP-0172: user_nickname # XEP-0172: user_nickname
if user_nick: if obj.user_nick:
msg_iq.setTag('nick', namespace = nbxmpp.NS_NICK).setData( msg_iq.setTag('nick', namespace=nbxmpp.NS_NICK).setData(
user_nick) obj.user_nick)
# XEP-0203 # XEP-0203
if delayed: if obj.delayed:
our_jid = gajim.get_jid_from_account(self.name) + '/' + \ our_jid = gajim.get_jid_from_account(self.name) + '/' + \
self.server_resource self.server_resource
timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(delayed)) timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(obj.delayed))
msg_iq.addChild('delay', namespace=nbxmpp.NS_DELAY2, msg_iq.addChild('delay', namespace=nbxmpp.NS_DELAY2,
attrs={'from': our_jid, 'stamp': timestamp}) attrs={'from': our_jid, 'stamp': timestamp})
# XEP-0224 # XEP-0224
if attention: if obj.attention:
msg_iq.setTag('attention', namespace=nbxmpp.NS_ATTENTION) msg_iq.setTag('attention', namespace=nbxmpp.NS_ATTENTION)
if isinstance(jid, list): if isinstance(obj.jid, list):
if self.addressing_supported: if self.addressing_supported:
msg_iq.setTo(gajim.config.get_per('accounts', self.name, 'hostname')) msg_iq.setTo(gajim.config.get_per('accounts', self.name, 'hostname'))
addresses = msg_iq.addChild('addresses', addresses = msg_iq.addChild('addresses',
namespace=nbxmpp.NS_ADDRESS) namespace=nbxmpp.NS_ADDRESS)
for j in jid: for j in obj.jid:
addresses.addChild('address', attrs = {'type': 'to', addresses.addChild('address', attrs = {'type': 'to',
'jid': j}) 'jid': j})
else: else:
iqs = [] iqs = []
for j in jid: for j in obj.jid:
iq = nbxmpp.Message(node=msg_iq) iq = nbxmpp.Message(node=msg_iq)
iq.setTo(j) iq.setTo(j)
iqs.append(iq) iqs.append(iq)
msg_iq = iqs msg_iq = iqs
else: else:
msg_iq.setTo(fjid) msg_iq.setTo(fjid)
r_ = resource r_ = obj.resource
if not r_ and jid != fjid: # Only if we're not in a pm if not r_ and obj.jid != fjid: # Only if we're not in a pm
r_ = gajim.get_resource_from_jid(fjid) r_ = gajim.get_resource_from_jid(fjid)
if r_: if r_:
contact = gajim.contacts.get_contact(self.name, jid, r_) contact = gajim.contacts.get_contact(self.name, obj.jid, r_)
else: else:
contact = gajim.contacts.get_contact_with_highest_priority( contact = gajim.contacts.get_contact_with_highest_priority(
self.name, jid) self.name, obj.jid)
# chatstates - if peer supports xep85, send chatstates # chatstates - if peer supports xep85, send chatstates
# please note that the only valid tag inside a message containing a # please note that the only valid tag inside a message containing a
# <body> tag is the active event # <body> tag is the active event
if chatstate and contact and contact.supports(nbxmpp.NS_CHATSTATES): if obj.chatstate and contact and contact.supports(nbxmpp.NS_CHATSTATES):
msg_iq.setTag(chatstate, namespace=nbxmpp.NS_CHATSTATES) msg_iq.setTag(obj.chatstate, namespace=nbxmpp.NS_CHATSTATES)
only_chatste = False only_chatste = False
if not msgtxt: if not msgtxt:
only_chatste = True only_chatste = True
if only_chatste and not session.enable_encryption: if only_chatste and not obj.session.enable_encryption:
msg_iq.setTag('no-store', msg_iq.setTag('no-store',
namespace=nbxmpp.NS_MSG_HINTS) namespace=nbxmpp.NS_MSG_HINTS)
@ -498,20 +468,20 @@ class CommonConnection:
nbxmpp.NS_RECEIPTS): nbxmpp.NS_RECEIPTS):
msg_iq.setTag('request', namespace=nbxmpp.NS_RECEIPTS) msg_iq.setTag('request', namespace=nbxmpp.NS_RECEIPTS)
if forward_from: if obj.forward_from:
addresses = msg_iq.addChild('addresses', addresses = msg_iq.addChild('addresses',
namespace=nbxmpp.NS_ADDRESS) namespace=nbxmpp.NS_ADDRESS)
addresses.addChild('address', attrs = {'type': 'ofrom', addresses.addChild('address', attrs = {'type': 'ofrom',
'jid': forward_from}) 'jid': obj.forward_from})
if session: if obj.session:
# XEP-0201 # XEP-0201
session.last_send = time.time() obj.session.last_send = time.time()
msg_iq.setThread(session.thread_id) msg_iq.setThread(obj.session.thread_id)
# XEP-0200 # XEP-0200
if session.enable_encryption: if obj.session.enable_encryption:
msg_iq = session.encrypt_stanza(msg_iq) msg_iq = obj.session.encrypt_stanza(msg_iq)
if self.carbons_enabled: if self.carbons_enabled:
msg_iq.addChild(name='private', msg_iq.addChild(name='private',
namespace=nbxmpp.NS_CARBONS) namespace=nbxmpp.NS_CARBONS)
@ -523,9 +493,19 @@ class CommonConnection:
msg_iq.addChild(name='no-store', msg_iq.addChild(name='no-store',
namespace=nbxmpp.NS_MSG_HINTS) namespace=nbxmpp.NS_MSG_HINTS)
if callback: self._push_stanza_message_outgoing(obj, msg_iq)
callback(jid, msg, keyID, forward_from, session, original_message,
subject, type_, msg_iq, xhtml) def _push_stanza_message_outgoing(self, obj, msg_iq):
obj.conn = self
if isinstance(msg_iq, list):
for iq in msg_iq:
obj.msg_iq = iq
gajim.nec.push_incoming_event(
StanzaMessageOutgoingEvent(None, **vars(obj)))
else:
obj.msg_iq = msg_iq
gajim.nec.push_incoming_event(
StanzaMessageOutgoingEvent(None, **vars(obj)))
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, additional_data=None): subject, type_, xhtml=None, additional_data=None):
@ -2142,58 +2122,35 @@ class Connection(CommonConnection, ConnectionHandlers):
if obj.account != self.name: if obj.account != self.name:
return return
# parameters of this function are packet into _cb_parameters for later usage in _nec_stanza_message_outgoing's cb() self._prepare_message(obj)
def cb(jid, msg, keyID, forward_from, session, original_message,
subject, type_, msg_iq, xhtml):
if isinstance(msg_iq, list):
for iq in msg_iq:
gajim.nec.push_incoming_event(StanzaMessageOutgoingEvent(
None, conn=self, msg_iq=iq, now=obj.now, automatic_message=obj.automatic_message, additional_data=obj.additional_data,
_cb_parameters={"jid":jid, "msg":msg, "keyID":keyID, "forward_from":forward_from,
"session":session, "original_message":original_message, "subject":subject, "type_":type_,
"msg_iq":msg_iq, "xhtml":xhtml, "obj":obj}))
else:
gajim.nec.push_incoming_event(StanzaMessageOutgoingEvent(None,
conn=self, msg_iq=msg_iq, now=obj.now, automatic_message=obj.automatic_message, additional_data=obj.additional_data,
_cb_parameters={"jid":jid, "msg":msg, "keyID":keyID, "forward_from":forward_from,
"session":session, "original_message":original_message, "subject":subject, "type_":type_,
"msg_iq":msg_iq, "xhtml":xhtml, "obj":obj}))
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
label=obj.label, session=obj.session, forward_from=obj.forward_from,
form_node=obj.form_node, original_message=obj.original_message,
delayed=obj.delayed, attention=obj.attention,
correction_msg=obj.correction_msg, callback=cb)
def _nec_stanza_message_outgoing(self, obj): def _nec_stanza_message_outgoing(self, obj):
if obj.conn.name != self.name: if obj.conn.name != self.name:
return return
obj.msg_id = self.connection.send(obj.msg_iq, now=obj.now) obj.msg_id = self.connection.send(obj.msg_iq, now=obj.now)
# obj in this function is the obj as seen in _nec_message_outgoing() gajim.nec.push_incoming_event(MessageSentEvent(
def cb(obj, jid, msg, keyID, forward_from, session, original_message, None, conn=self, jid=obj.jid, message=obj.message, keyID=obj.keyID,
subject, type_, msg_iq, xhtml, msg_id): chatstate=obj.chatstate, automatic_message=obj.automatic_message,
gajim.nec.push_incoming_event(MessageSentEvent(None, conn=self, msg_id=obj.msg_id, additional_data=obj.additional_data))
jid=jid, message=msg, keyID=keyID, chatstate=obj.chatstate,
automatic_message=obj.automatic_message, msg_id=msg_id, additional_data=obj.additional_data))
if obj.callback: if obj.callback:
obj.callback(obj, msg_iq, *obj.callback_args) obj.callback(obj, obj.msg_iq, *obj.callback_args)
if not obj.is_loggable: if not obj.is_loggable:
return return
if isinstance(jid, list): if isinstance(obj.jid, list):
for j in jid: for j in obj.jid:
if session is None: if obj.session is None:
session = self.get_or_create_session(j, '') obj.session = self.get_or_create_session(j, '')
self.log_message(j, msg, forward_from, session, self.log_message(
original_message, subject, type_, xhtml, obj.additional_data) j, obj.message, obj.forward_from, obj.session,
obj.original_message, obj.subject, obj.type_, obj.xhtml,
obj.additional_data)
else: else:
self.log_message(jid, msg, forward_from, session, self.log_message(
original_message, subject, type_, xhtml, obj.additional_data) obj.jid, obj.message, obj.forward_from, obj.session,
obj.original_message, obj.subject, obj.type_, obj.xhtml,
cb(msg_id=obj.msg_id, **obj._cb_parameters) obj.additional_data)
def send_contacts(self, contacts, fjid, type_='message'): def send_contacts(self, contacts, fjid, type_='message'):
""" """

View file

@ -2749,6 +2749,13 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
self.correction_msg = None self.correction_msg = None
self.automatic_message = True self.automatic_message = True
def get_full_jid(self):
if self.resource:
return self.jid + '/' + self.resource
if self.session:
return self.session.get_to()
return self.jid
def generate(self): def generate(self):
return True return True

View file

@ -64,8 +64,8 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
CommonConnection.__init__(self, name) CommonConnection.__init__(self, name)
self.is_zeroconf = True self.is_zeroconf = True
gajim.ged.register_event_handler('message-outgoing', ged.OUT_CORE, gajim.ged.register_event_handler('stanza-message-outgoing', ged.OUT_CORE,
self._nec_message_outgoing) self._nec_stanza_message_outgoing)
def get_config_values_or_default(self): def get_config_values_or_default(self):
""" """
@ -332,19 +332,15 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
title=_('Could not change status of account "%s"') % self.name, title=_('Could not change status of account "%s"') % self.name,
msg=_('Please check if avahi-daemon is running.'))) msg=_('Please check if avahi-daemon is running.')))
def _nec_message_outgoing(self, obj): def _nec_stanza_message_outgoing(self, obj):
if obj.account != self.name:
return
def cb(jid, msg, keyID, forward_from, session, original_message, subject,
type_, msg_iq, xhtml):
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,
jid=obj.jid, message=obj.message, keyID=obj.keyID, jid=obj.jid, message=obj.message, keyID=obj.keyID,
automatic_message=obj.automatic_message, chatstate=None, automatic_message=obj.automatic_message, chatstate=None,
msg_id=msg_id)) msg_id=msg_id))
if obj.callback: if obj.callback:
obj.callback(msg_iq, *obj.callback_args) obj.callback(obj.msg_iq, *obj.callback_args)
if not obj.is_loggable: if not obj.is_loggable:
return return
@ -357,22 +353,16 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
fjid=obj.jid, error_code=-1, error_msg=reason, msg=None, fjid=obj.jid, error_code=-1, error_msg=reason, msg=None,
time_=None, session=obj.session)) time_=None, session=obj.session))
ret = self.connection.send(msg_iq, msg is not None, on_ok=on_send_ok, ret = self.connection.send(
on_not_ok=on_send_not_ok) obj.msg_iq, obj.message is not None,
on_ok=on_send_ok, on_not_ok=on_send_not_ok)
if ret == -1: if ret == -1:
# Contact Offline # Contact Offline
gajim.nec.push_incoming_event(MessageErrorEvent(None, conn=self, gajim.nec.push_incoming_event(MessageErrorEvent(None, conn=self,
fjid=jid, error_code=-1, error_msg=_( fjid=obj.jid, error_code=-1, error_msg=_(
'Contact is offline. Your message could not be sent.'), 'Contact is offline. Your message could not be sent.'),
msg=None, time_=None, session=session)) msg=None, time_=None, session=obj.session))
self._prepare_message(obj.jid, obj.message, obj.keyID, type_=obj.type_,
subject=obj.subject, chatstate=obj.chatstate, msg_id=obj.msg_id,
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
label=obj.label, session=obj.session, forward_from=obj.forward_from,
form_node=obj.form_node, original_message=obj.original_message,
delayed=obj.delayed, attention=obj.attention, callback=cb)
def send_stanza(self, stanza): def send_stanza(self, stanza):
# send a stanza untouched # send a stanza untouched