Tutorial: Chat Service¶
This page walks you through the full process of building the backend for a simple P2P Chat application.
In this example we’ll build a class called Chat which can be used
by a front-end GUI. We’ll start with the basics of initializing the
API and connecting to a Network; see the QuickStart
for more detail on this process.
import json
import time
from pipes import PlatformAPI
class Chat(object):
def __init__(self, apiPath):
self.api = PlatformAPI(apiPath)
self.node = None
self._listener = None
def isUserConfigured(self):
return self.api.hasLocalIdentity()
def setupUser(self, userName):
if self.isUserConfigured():
raise Exception("User is already configured: {}".format(self.api.localIdentity().name))
self.api.addIdentity(userName)
def connectToNetwork(self, networkId):
if self.node:
raise Exception("Already connected!")
network = self.api.networkWithId(networkId)
self.identity = self.api.localIdentity()
self.node = network.nodeWithIdentity(self.identity)
self._setupPipeline()
self.node.connect()
This provides an API for configuring the user name and the basics of
connecting to a network. This assumes that the indicated networkId
is already configured in the API; see the QuickStart
for more detail on this process.
We need to define the _setupPipeline() method, which configures a
Pipeline on the network for use with our chat application:
def _setupPipeline(self):
self.pipeline = self.node.pipelineNamed('CHAT')
if not self.pipeline:
self.pipeline = self.node.createPipeline(name = 'CHAT')
self.pipeline.addPacketListener(self._packetListener)
And finally, we need to manage the sending and receiving of messages. For simplicity we use JSON payloads for the messages:
def sendMessage(self, text):
assert(self.node)
message = { "text" : text, "sender" : self.identity.name, "timestamp" : time.time() }
self.pipeline.putPacket(json.dumps(message).encode())
def _packetListener(self, packet, pipeline):
message = json.loads(packet.data.decode())
if self._listener:
self._listener(message)
def setListener(self, listener):
self._listener = listener
The sendMessage method creates a JSON dictionary using the
indicated text, the local username, and a timestamp to indicate the
time the message was entered. This is then placed on the pipeline
using the putPacket method.
The _packetListener method is attached to the Pipeline, and is
called anytime a Packet is put on the pipeline, from any Node in the
Network. This decodes the JSON dictionary from the packet and passes
it on to the UI listener, which is configured using setListener().
So, for a UI to use this backend, it simply needs to:
Create an instance of the
ChatserviceInitialize it with the proper path, and do some one-time setup including querying the user for a username
Use
setListener()to receive callbacks whenever messages are availableUse
sendMessage()to send a chat from the local user upon request
The Pipes API takes care of connecting the Nodes and routing Packets between them. This Chat application will already support an arbitrary number of Nodes. As it is now, messages are only received if you are on the Network when they are sent; the next step is to use persistent Pipelines to ensure messages are always delivered.
Persistent Chat¶
For persistent chat, we only need to change the one line in the
_setupPipeline method where we configure the pipeline:
self.pipeline = self.node.createPipeline(name = 'CHAT', persistent = True)
That’s it! This backend now guarantees that chats are published to the Network and delivered to all Nodes as soon as possible. This includes offline work: if you send a chat when your Node is not connected, it will be cached locally and delivered when you next connect.
History and Search¶
TODO: state caching
Multiple Channels¶
TODO: brief overview
Private Chat¶
TODO: using a point-to-point pipeline
Encrypted Chat¶
TODO: using encrypted Pipelines and sharing secrets