plugin XEP-0334: fixed and improved message processing hints:
- wrong elements were used in this plugging, it could not work properly
- use constants for hints
- add addHint method so other plugins can add a hint easily
- hints can be added by frontends in mess_data['extra']['hints'] (serialized dict) or by other plugins using addHint
- if history is skipped on message reception, mess_data['extra']['history']['skipped'] is set
- introduced PLUGIN_INFO['usage'] for usage information for developers
This commit is contained in:
parent
13e2e50cb0
commit
a2c151f9c6
2 changed files with 63 additions and 55 deletions
|
|
@ -672,7 +672,7 @@ class SAT(service.Service):
|
||||||
@param data: message data dictionnary
|
@param data: message data dictionnary
|
||||||
@param client: profile's client
|
@param client: profile's client
|
||||||
"""
|
"""
|
||||||
client.send(data['xml'])
|
client.send(data[u'xml'])
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def messageAddToHistory(self, data, client):
|
def messageAddToHistory(self, data, client):
|
||||||
|
|
@ -681,10 +681,10 @@ class SAT(service.Service):
|
||||||
@param data: message data dictionnary
|
@param data: message data dictionnary
|
||||||
@param client: profile's client
|
@param client: profile's client
|
||||||
"""
|
"""
|
||||||
if data["type"] != C.MESS_TYPE_GROUPCHAT:
|
if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
|
||||||
# we don't add groupchat message to history, as we get them back
|
# we don't add groupchat message to history, as we get them back
|
||||||
# and they will be added then
|
# and they will be added then
|
||||||
if data['message'] or data['subject']: # we need a message to store
|
if data[u'message'] or data[u'subject']: # we need a message to store
|
||||||
self.memory.addToHistory(client, data)
|
self.memory.addToHistory(client, data)
|
||||||
else:
|
else:
|
||||||
log.warning(u"No message found") # empty body should be managed by plugins before this point
|
log.warning(u"No message found") # empty body should be managed by plugins before this point
|
||||||
|
|
@ -696,14 +696,14 @@ class SAT(service.Service):
|
||||||
@param data: message data dictionnary
|
@param data: message data dictionnary
|
||||||
@param client: profile's client
|
@param client: profile's client
|
||||||
"""
|
"""
|
||||||
if data["type"] != C.MESS_TYPE_GROUPCHAT:
|
if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
|
||||||
# we don't send groupchat message to bridge, as we get them back
|
# we don't send groupchat message to bridge, as we get them back
|
||||||
# and they will be added the
|
# and they will be added the
|
||||||
if data['message'] or data['subject']: # we need a message to send something
|
if data[u'message'] or data[u'subject']: # we need a message to send something
|
||||||
# We send back the message, so all frontends are aware of it
|
# We send back the message, so all frontends are aware of it
|
||||||
self.bridge.messageNew(data['uid'], data['timestamp'], data['from'].full(), data['to'].full(), data['message'], data['subject'], data['type'], data['extra'], profile=client.profile)
|
self.bridge.messageNew(data[u'uid'], data[u'timestamp'], data[u'from'].full(), data[u'to'].full(), data[u'message'], data[u'subject'], data[u'type'], data[u'extra'], profile=client.profile)
|
||||||
else:
|
else:
|
||||||
log.warning(_("No message found"))
|
log.warning(_(u"No message found"))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
|
def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from sat.core.i18n import _
|
from sat.core.i18n import _, D_
|
||||||
from sat.core.log import getLogger
|
from sat.core.log import getLogger
|
||||||
log = getLogger(__name__)
|
log = getLogger(__name__)
|
||||||
|
|
||||||
from sat.core import exceptions
|
from sat.core import exceptions
|
||||||
|
from sat.tools.common import data_format
|
||||||
|
|
||||||
from wokkel import disco, iwokkel
|
from wokkel import disco, iwokkel
|
||||||
try:
|
try:
|
||||||
|
|
@ -31,81 +32,88 @@ except ImportError:
|
||||||
from wokkel.subprotocols import XMPPHandler
|
from wokkel.subprotocols import XMPPHandler
|
||||||
from twisted.python import failure
|
from twisted.python import failure
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
|
||||||
NS_MPH = 'urn:xmpp:hints'
|
|
||||||
|
|
||||||
PLUGIN_INFO = {
|
PLUGIN_INFO = {
|
||||||
"name": "Message Processing Hints",
|
"name": u"Message Processing Hints",
|
||||||
"import_name": "XEP-0334",
|
"import_name": u"XEP-0334",
|
||||||
"type": "XEP",
|
"type": u"XEP",
|
||||||
"protocols": ["XEP-0334"],
|
"protocols": [u"XEP-0334"],
|
||||||
"main": "XEP_0334",
|
"main": "XEP_0334",
|
||||||
"handler": "yes",
|
"handler": u"yes",
|
||||||
"description": _("""Implementation of Message Processing Hints""")
|
"description": D_(u"""Implementation of Message Processing Hints"""),
|
||||||
|
"usage": dedent(D_(u"""\
|
||||||
|
Frontends can use HINT_* constants in mess_data['extra'] in a serialized 'hints' dict.
|
||||||
|
Internal plugins can use directly addHint([HINT_* constant]).
|
||||||
|
Will set mess_data['extra']['history'] to 'skipped' when no store is requested and message is not saved in history."""))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_HINTS = u'urn:xmpp:hints'
|
||||||
|
|
||||||
|
|
||||||
class XEP_0334(object):
|
class XEP_0334(object):
|
||||||
|
HINT_NO_PERMANENT_STORE = u'no-permanent-store'
|
||||||
|
HINT_NO_STORE = u'no-store'
|
||||||
|
HINT_NO_COPY = u'no-copy'
|
||||||
|
HINT_STORE = u'store'
|
||||||
|
HINTS = (HINT_NO_PERMANENT_STORE, HINT_NO_STORE, HINT_NO_COPY, HINT_STORE)
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
log.info(_("Message Processing Hints plugin initialization"))
|
log.info(_("Message Processing Hints plugin initialization"))
|
||||||
self.host = host
|
self.host = host
|
||||||
host.trigger.add("messageSend", self.messageSendTrigger)
|
host.trigger.add("messageSend", self.messageSendTrigger)
|
||||||
host.trigger.add("MessageReceived", self.messageReceivedTrigger)
|
host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000)
|
||||||
|
|
||||||
def getHandler(self, profile):
|
def getHandler(self, profile):
|
||||||
return XEP_0334_handler(self, profile)
|
return XEP_0334_handler()
|
||||||
|
|
||||||
|
def addHint(self, mess_data, hint):
|
||||||
|
if hint == self.HINT_NO_COPY and not mess_data['to'].resource:
|
||||||
|
log.error(u"{hint} can only be used with full jids! Ignoring it.".format(hint=hint))
|
||||||
|
return
|
||||||
|
hints = mess_data.setdefault('hints', set())
|
||||||
|
if hint in self.HINTS:
|
||||||
|
hints.add(hint)
|
||||||
|
else:
|
||||||
|
log.error(u"Unknown hint: {}".format(hint))
|
||||||
|
|
||||||
|
def _sendPostXmlTreatment(self, mess_data):
|
||||||
|
if 'hints' in mess_data:
|
||||||
|
for hint in mess_data['hints']:
|
||||||
|
mess_data[u'xml'].addElement((NS_HINTS, hint))
|
||||||
|
return mess_data
|
||||||
|
|
||||||
def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
|
def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
|
||||||
"""Add the hints element to the message to be sent"""
|
"""Add the hints element to the message to be sent"""
|
||||||
hints = []
|
if u'hints' in mess_data[u'extra']:
|
||||||
for key in ('no-permanent-storage', 'no-storage', 'no-copy'):
|
for hint in data_format.dict2iter(u'hints', mess_data[u'extra'], pop=True):
|
||||||
if mess_data['extra'].get(key, None):
|
self.addHint(hint)
|
||||||
hints.append(key)
|
|
||||||
|
|
||||||
def treatment(mess_data):
|
post_xml_treatments.addCallback(self._sendPostXmlTreatment)
|
||||||
message = mess_data['xml']
|
|
||||||
for key in hints:
|
|
||||||
message.addElement((NS_MPH, key))
|
|
||||||
if key in ('no-permanent-storage', 'no-storage'):
|
|
||||||
mess_data['extra']['no_storage'] = True
|
|
||||||
# TODO: the core doesn't process this 'no_storage' info yet
|
|
||||||
# it will be added after the frontends refactorization
|
|
||||||
return mess_data
|
|
||||||
|
|
||||||
if hints:
|
|
||||||
post_xml_treatments.addCallback(treatment)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def messageReceivedTrigger(self, client, message, post_treat):
|
def _receivedSkipHistory(self, mess_data):
|
||||||
|
mess_data[u'extra'][u'history'] == u'skipped'
|
||||||
|
raise failure.Failure(exceptions.SkipHistory())
|
||||||
|
|
||||||
|
|
||||||
|
def messageReceivedTrigger(self, client, message_elt, post_treat):
|
||||||
"""Check for hints in the received message"""
|
"""Check for hints in the received message"""
|
||||||
hints = []
|
for elt in message_elt.elements():
|
||||||
for key in ('no-permanent-storage', 'no-storage'):
|
if elt.uri == NS_HINTS and elt.name in (self.HINT_NO_PERMANENT_STORE, self.HINT_NO_STORE):
|
||||||
try:
|
log.debug(u"history will be skipped for this message, as requested")
|
||||||
message.elements(uri=NS_MPH, name=key).next()
|
post_treat.addCallback(self._receivedSkipHistory)
|
||||||
hints.append(key)
|
break
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def post_treat_hints(data):
|
|
||||||
raise failure.Failure(exceptions.SkipHistory())
|
|
||||||
|
|
||||||
if hints:
|
|
||||||
post_treat.addCallback(post_treat_hints)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class XEP_0334_handler(XMPPHandler):
|
class XEP_0334_handler(XMPPHandler):
|
||||||
implements(iwokkel.IDisco)
|
implements(iwokkel.IDisco)
|
||||||
|
|
||||||
def __init__(self, plugin_parent, profile):
|
|
||||||
self.plugin_parent = plugin_parent
|
|
||||||
self.host = plugin_parent.host
|
|
||||||
self.profile = profile
|
|
||||||
|
|
||||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||||
return [disco.DiscoFeature(NS_MPH)]
|
return [disco.DiscoFeature(NS_HINTS)]
|
||||||
|
|
||||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue