plugin XEP-0199: implementation of XMPP Ping

This commit is contained in:
Goffi 2018-11-10 10:16:38 +01:00
commit 3a07122da9
5 changed files with 178 additions and 11 deletions

View file

@ -5,6 +5,7 @@ v 0.7.0 (NOT RELEASED YET):
This is also the first "general audience" version.
- XEP-0070 implementation (HTTP Auth via XMPP)
- XEP-0184 implementation (Delivery Receipts)
- XEP-0199 implementation (XMPP Ping)
- XEP-0231 implementation (Bits of Binary)
- XEP-0264 implementation (Thumbnails)
- XEP-0280 implementation (Mesage Carbons)

View file

@ -606,7 +606,10 @@ class SAT(service.Service):
def getSessionInfos(self, profile_key):
"""compile interesting data on current profile session"""
client = self.getClient(profile_key)
data = {"jid": client.jid.full(), "started": unicode(int(client.started))}
data = {
"jid": client.jid.full(),
"started": unicode(int(client.started))
}
return defer.succeed(data)
# local dirs

View file

@ -57,7 +57,8 @@ class TextCommands(object):
# should be downloadable independently)
HELP_SUGGESTION = _(
"Type '/help' to get a list of the available commands. If you didn't want to use a command, please start your message with '//' to escape the slash."
u"Type '/help' to get a list of the available commands. If you didn't want to "
u"use a command, please start your message with '//' to escape the slash."
)
def __init__(self, host):
@ -75,7 +76,8 @@ class TextCommands(object):
@param cmd: function or method callback for the command,
its docstring will be used for self documentation in the following way:
- first line is the command short documentation, shown with /help
- @command keyword can be used, see http://wiki.goffi.org/wiki/Coding_style/en for documentation
- @command keyword can be used,
see http://wiki.goffi.org/wiki/Coding_style/en for documentation
@return (dict): dictionary with parsed data where key can be:
- "doc_short_help" (default: ""): the untranslated short documentation
- "type" (default "all"): the command type as specified in documentation
@ -177,10 +179,12 @@ class TextCommands(object):
"""Add a callback which give information to the /whois command
@param callback: a callback which will be called with the following arguments
- whois_msg: list of information strings to display, callback need to append its own strings to it
- whois_msg: list of information strings to display, callback need to append
its own strings to it
- target_jid: full jid from whom we want information
- profile: %(doc_profile)s
@param priority: priority of the information to show (the highest priority will be displayed first)
@param priority: priority of the information to show (the highest priority will
be displayed first)
"""
self._whois.append((priority, callback))
self._whois.sort(key=lambda item: item[0], reverse=True)
@ -195,11 +199,14 @@ class TextCommands(object):
def _sendMessageCmdHook(self, mess_data, client):
""" Check text commands in message, and react consequently
msg starting with / are potential command. If a command is found, it is executed, else and help message is sent
msg starting with / are potential command. If a command is found, it is executed,
else an help message is sent.
msg starting with // are escaped: they are sent with a single /
commands can abord message sending (if they return anything evaluating to False), or continue it (if they return True), eventually after modifying the message
an "unparsed" key is added to message, containing part of the message not yet parsed
commands can be deferred or not
commands can abord message sending (if they return anything evaluating to False),
or continue it (if they return True), eventually after modifying the message
an "unparsed" key is added to message, containing part of the message not yet
parsed.
Commands can be deferred or not
@param mess_data(dict): data comming from sendMessage trigger
@param profile: %(doc_profile)s
"""

View file

@ -472,8 +472,8 @@ class XEP_0045(object):
return muc_client
def kick(self, client, nick, room_jid, options=None):
"""
Kick a participant from the room
"""Kick a participant from the room
@param nick (str): nick of the user to kick
@param room_jid_s (JID): jid of the room
@param options (dict): attribute with extra info (reason, password) as in #XEP-0045

View file

@ -0,0 +1,156 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# SAT plugin for Delayed Delivery (XEP-0199)
# Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
# Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# 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/>.
from sat.core.i18n import _, D_
from sat.core.log import getLogger
log = getLogger(__name__)
from sat.core.constants import Const as C
from wokkel import disco, iwokkel
from twisted.words.protocols.jabber import xmlstream, jid
from zope.interface import implements
import time
PLUGIN_INFO = {
C.PI_NAME: u"XMPP PING",
C.PI_IMPORT_NAME: u"XEP-0199",
C.PI_TYPE: u"XEP",
C.PI_PROTOCOLS: [u"XEP-199"],
C.PI_MAIN: "XEP_0199",
C.PI_HANDLER: u"yes",
C.PI_DESCRIPTION: D_(u"""Implementation of XMPP Ping"""),
}
NS_PING = u"urn:xmpp:ping"
PING_REQUEST = C.IQ_GET + '/ping[@xmlns="' + NS_PING + '"]'
class XEP_0199(object):
def __init__(self, host):
log.info(_("XMPP Ping plugin initialization"))
self.host = host
host.bridge.addMethod(
"ping", ".plugin", in_sign='ss', out_sign='d', method=self._ping, async=True)
try:
self.text_cmds = self.host.plugins[C.TEXT_CMDS]
except KeyError:
log.info(_(u"Text commands not available"))
else:
self.text_cmds.registerTextCommands(self)
def getHandler(self, client):
return XEP_0199_handler(self)
def _pingRaiseIfFailure(self, pong):
"""If ping didn't succeed, raise the failure, else return pong delay"""
if pong[0] != u"PONG":
raise pong[0]
return pong[1]
def _ping(self, jid_s, profile):
client = self.host.getClient(profile)
entity_jid = jid.JID(jid_s)
d = self.ping(client, entity_jid)
d.addCallback(self._pingRaiseIfFailure)
return d
def _pingCb(self, iq_result, send_time):
receive_time = time.time()
return (u"PONG", receive_time - send_time)
def _pingEb(self, failure_, send_time):
receive_time = time.time()
return (failure_.value, receive_time - send_time)
def ping(self, client, entity_jid):
"""Ping an XMPP entity
@param entity_jid(jid.JID): entity to ping
@return (tuple[(unicode,failure), float]): pong data:
- either u"PONG" if it was successful, or failure
- delay between sending time and reception time
"""
iq_elt = client.IQ("get")
iq_elt["to"] = entity_jid.full()
iq_elt.addElement((NS_PING, "ping"))
d = iq_elt.send()
send_time = time.time()
d.addCallback(self._pingCb, send_time)
d.addErrback(self._pingEb, send_time)
return d
def _cmd_ping_fb(self, pong, client, mess_data):
"""Send feedback to client when pong data is received"""
txt_cmd = self.host.plugins[C.TEXT_CMDS]
if pong[0] == u"PONG":
txt_cmd.feedBack(client, u"PONG ({time} s)".format(time=pong[1]), mess_data)
else:
txt_cmd.feedBack(
client, _(u"ping error ({err_msg}). Response time: {time} s")
.format(err_msg=pong[0], time=pong[1]), mess_data)
def cmd_ping(self, client, mess_data):
"""ping an entity
@command (all): [JID]
- JID: jid of the entity to ping
"""
if mess_data["unparsed"].strip():
try:
entity_jid = jid.JID(mess_data["unparsed"].strip())
except RuntimeError:
txt_cmd = self.host.plugins[C.TEXT_CMDS]
txt_cmd.feedBack(client, _(u'Invalid jid: "{entity_jid}"').format(
entity_jid=mess_data["unparsed"].strip()), mess_data)
return False
else:
entity_jid = mess_data["to"]
d = self.ping(client, entity_jid)
d.addCallback(self._cmd_ping_fb, client, mess_data)
return False
def onPingRequest(self, iq_elt, client):
log.info(_(u"XMPP PING received from {from_jid} [{profile}]").format(
from_jid=iq_elt["from"], profile=client.profile))
iq_elt.handled = True
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
client.send(iq_result_elt)
class XEP_0199_handler(xmlstream.XMPPHandler):
implements(iwokkel.IDisco)
def __init__(self, plugin_parent):
self.plugin_parent = plugin_parent
def connectionInitialized(self):
self.xmlstream.addObserver(
PING_REQUEST, self.plugin_parent.onPingRequest, client=self.parent
)
def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
return [disco.DiscoFeature(NS_PING)]
def getDiscoItems(self, requestor, target, nodeIdentifier=""):
return []