Plugins architecture

De Goffiwiki
Aller à : navigation, rechercher


SàT use a plugin architecture on the backend side, which allow to easily extends the functionality, with XMPP extensions or things which have nothing to do with XMPP.


Plugin file

A plugin file must be named plugin_XXX.py and be placed in src/plugins directory. They will be automatically loaded by the backend.

The plugins related to XEP should be named plugin_xep_xxxx.py, with xxxx being the XEP number, but this is not mandatory.
Experimental plugins should be named plugin_exp_xxx.py
General purpose plugins should be named plugin_misc_xxx.py

plugin info

A plugin has a header in the following form (here is the plugin_xep_0115.py example):

PLUGIN_INFO = { 
"name": "XEP 0115 Plugin",
"import_name": "XEP-0115",
"type": "XEP",
"protocols": ["XEP-0115"],
"dependencies": [],
"main": "XEP_0115",
"handler": "yes",
"description": D_(u"""Implementation of entity capabilities""")
}
  • name is the human readable name of the plugin
  • import_name is a short name used to reference it from other plugins
  • type can be currently one of XEP, Misc, Game, EXP for respectively XMPP Extension, Miscelanous, Game, Experimental
  • protocols list the protocols managed by this plugin
  • dependencies list the others plugins needed to use this one. The dependencies are mandatory, and plugin will not be loaded if they are not available. Dependencies are loaded before this plugin, so they will be available when main class' __init__ is called.
  • recommendations list the others plugins that this plugin may use for additional features. The recommendations are not mandatory, and plugin is loaded even if they are not available. Recommentations are loaded before this plugin, so they will be available (if present) when main class's __init__ is called. A typical exemple of recommendation is text commands plugin: if C.TEXT_CMDS in PLUGIN_INFO['recommendations'], then plugin text commands will be loaded if available, and plugin can check it's presence to add its own commands.
  • handler: if yes, a handler will be created on profile connection, the plugin need to had a getHandler(self, profile) method.
  • description: human readable full description of the plugin
  • usage: human instructions on how to use the plugin for developers, when relevant

The plugin can also have a profileConnected(self, profile) method: if present, it will be called on profile connection, which can be useful to initiate some profile-specific things. Similarly profileDisconnected(self, profile) can be used to free profile-specific resources.

Plugin can declare available features by implementing a getFeatures method (called with profile as argument, which can be C.PROF_KEY_NONE to get general informations). This can be useful to frontend as an indicator of graphic elements to show/hide/disable.

Managing disco

Declaring features

Service Discovery (XEP-0030) is essential to manage XMPP XEPs into plugin. When you implement a XEP, you declare the features implemented with an handler. So first you must set the handler value in PLUGIN_INGO to 'yes'. Then you must create a subclass of XMPPHandler, and return an instance in the getHandler method. This class must implement iwokkel.IDisco, and its getDiscoInfo must return a list with the implemented features.

Here is an exemple for XEP-0071 (XHTML-IM):

NS_XHTML_IM = 'http://jabber.org/protocol/xhtml-im'

PLUGIN_INFO = {
    ...
    "main": "XEP_0071",
    "handler": "yes",
    ...
}


class XEP_0071(object):

    ...
    def getHandler(self, profile):
        return XEP_0071_handler()
    ...


class XEP_0071_handler(XMPPHandler):
    implements(iwokkel.IDisco)

    def getDiscoInfo(self, requestor, target, nodeIdentifier=):
        return [disco.DiscoFeature(NS_XHTML_IM)]

    def getDiscoItems(self, requestor, target, nodeIdentifier=):
        return []

checking features or request features set

To easily check that the entity you are talking to manage the needed features, a couple of methods are available in memory.disco, and shortcuts are directly callable throught SAT (self.host):

  • hasFeature return a Deferred which fire True or False if the feature is available
  • checkFeature do the same but raise and exception if feature is not found, instead of returning a boolean
  • findServiceEntities return all available items of an entity which correspond to a category and a type
  • findFeaturesSet return a set of entities (including requested jid and its items) offering requested features (it is also possible to filter with a category and/or a type)
  • getDiscoInfos (or memory.disco.getInfos) return whole disco infos
  • getDiscoItems (or memory.disco.getItems) return available items.

memory.disco also manage a cache, to avoid requesting informations each time it's needed. If you need to avoid the cache, you can use wokkel's disco module directly.