Skip to content

Architecture

Tarek edited this page Apr 22, 2019 · 9 revisions

The architecture

Stacks and Layers

A running Yowsup app is a stack of several layers. A layer is a bidirectional channel that transforms the data passing through it before passing it to underlying or the above layer. Here are a few things you should get introduced to:

YowLayer

This is the base class for all Yowsup layers. A layer should implement at least a send method and a receive method. The receive method is called for data coming from the layer below, and the send method is called for data coming from the layer above.

Here is an example pass-through layer that you can place anywhere in the stack and it just passes through the data it gets from above and below:

class PassthroughLayer(YowLayer):
    def send(self, data):
        self.toLower(data)

    def receive(self, data):
        self.toUpper(data)
YowLayerEvent

YowLayers also are able to send and receive events to and from other layers.

self.emitEvent(YowLayerEvent)  # sends event to upper layers
self.broadcastEvent(YowLayerEvent)  # sends event to lower layers

To handle events in one layer, you need to implement the onEvent method in that layer:

class PassthroughLayer(YowLayer):
    def send(self, data):
        self.toLower(data)

    def receive(self, data):
        self.toUpper(data)
    
    def onEvent(self, yowLayerEvent):
        if yowLayerEvent.getName() == "disconnect":
            '''disconnect or whatever'''
            return True  # if you return `True`, the event will not propagate any further

Now let's analyze the stack in the figure.

YowNetworkLayer

Responsible for reading (receiving) incoming data from the server, and writing (sending) data to it.

When sending, it expects data from YowStanzaRegulatorLayer of type bytearray.

YowCoderLayer

When receiving, data arriving at YowCoderLayer is decrypted data. It is now YowCoderLayer's job to map those decrypted data to meaningful XML stanzas, represented by a Yowsup class called ProtocolTreeNode. It then passes these ProtocolTreeNode instances to whatever layer placed above it.

When sending, YowCoderLayer also expects to get from whatever layers placed on top of it an instance of ProtocolTreeNode. It then takes care of transforming it to bytes before passing it downwards to YowNoiseLayer.

Special Layer types:

While general layers should just subclass YowLayer, there are also cases where a group of layers share similar functionality. In such cases it is useful to extend the following more specific YowLayer subclasses:

YowProtocolLayer

A protocol layer in Yowsup that expects ProtocolTreeNode from the layer below, and transmits an instance of ProtocolEntity to the layer above. YowProtocolLayer adds convencience methods for any layer which follows this pattern. Therefore they should subclass YowProtocolLayer instead of YowLayer.

ProtocolEntity

A protocol entity is a mapping from the generic ProtocolTreeNode XML representation to a real object with attributes representing the stanza.

For example, TextMessageProtocolEntity provides you with methods like getBody that directly returns the message body instance for getting your hands dirty with XML nodes traversing.

For each type of an XML stanza there should be implemented an associated ProtocolEntity subclass.

YowInterfaceLayer

A YowInterfaceLayer layer is a layer that expects to receive an instance of ProtocolEntity from below. Subclassing YowInterfaceLayer will provide you with convenience methods like connect, disconnect, and others. A YowInterfaceLayer is what you usually need to implement to integrate Yowsup in any project.

@ProtocolEntityCallback(str)

When you subclass YowInterfaceLayer, you can then make use this decorator

@ProtocolEntityCallback("message")
def onMessage(messageProtocolEntity):
    '''do stuff'''

This basically means, whenever your layer receives data (instances of ProtocolEntity), if this object's getTag method returned "message", the onMessage method will be automatically invoked with this object passed as an argument.

So as "message" is basically the value returned by a protocolEntity.getTag(), those are the other main possible values as well:

  • message (for both text and media messages)
  • iq
  • ib
  • notification
  • presence
  • receipt
  • ack

Please note that, for you to actually be able to send and receive specific type of data -- notification for example -- the associated YowNotificationsProtocolLayer must exist in your stack.

Parallel Layers

Yowsup stack also supports parallel layers. That is, several layers at the same level in the stack, all getting the same exact data from the layer above and below. In Yowsup, protocol layers are placed in parallel, so the final stack becomes something similar to this:

YowAuthenticatorLayer, YowMessagesProtocolLayer, YowGroupsProtocolLayer, YowReceiptProtocolLayer, YowPresenceProtocolLayer ..etc
YowCoderLayer,
YowNoiseLayer,
YowNoiseSegmentsLayer,
YowNetworklayer

In this scenario, the list of layers at the same level -- YowAuthenticatorLayer, YowMessagesProtocolLayer, YowGroupsProtocolLayer, YowReceiptProtocolLayer, and YowPresenceProtocolLayer -- are all getting the exact data from YowCoderLayer, which is an instance of ProtocolTreeNode, and transforming it into an instance of some subclass of ProtocolEntity. Each layer is responsible for its own set of stanza types and transforming it into some ProtocolEntity. They are all also getting the same ProtocolEntity instance from the layer above and they're responsible for transforming into a ProtocolTreeNode, the format accepted by the YowCoderLayer below.