Brian
Brian

Reputation: 427

How to send message when file changes detected? Twisted and Web Sockets

I am currently trying to create a small demo where I connect a web socket between my computer and my localhost ws://localhost:8080/ws. I want the web socket to monitor a file on my computer for changes. If there are changes, send a message. The connection and output is being monitored using Advanced Rest Client.

Is there a specific method I can use on the class to constantly check the contents of this file?

EDIT

I have implemented an observer using watchdog that detects any event for files in a specified directory. However, my message is not being sent in the sendFSEvent method and I also realized that my client isn't being registered when I connect to the web socket.

Here is my code in server.py

import sys
import os

from watchdog.observers import Observer
from twisted.web.static import File
from twisted.python import log
from twisted.web.server import Site
from twisted.internet import reactor, defer

from autobahn.twisted.websocket import WebSocketServerFactory, \
    WebSocketServerProtocol, listenWS

from MessangerEventHandler import MessangerEventHandler


class WsProtocol(WebSocketServerProtocol):
    def connectionMade(self):
        print("Connection made")
        WebSocketServerProtocol.connectionMade(self)

    def onOpen(self):
        WebSocketServerProtocol.onOpen(self)
        print("WebSocket connection open")

    def onMessage(self, payload, isBinary):
        print("Message was: {}".format(payload))
        self.sendMessage("message received")

    def sendFSEvent(self, json):
        WebSocketProtocol.sendMessage(self, json)
        print('Sent FS event')

    def onClose(self, wasClean, code, reason):
        print("Connection closed: {}".format(reason))
        WebSocketServerProtocol.onClose(self, wasClean, code, reason)


class WsServerFactory(WebSocketServerFactory):
    protocol = WsProtocol

    def __init__(self, url='ws://localhost', port=8080):
        addr = url + ':' + str(port)
        print("Listening on: {}".format(addr))
        WebSocketServerFactory.__init__(self, addr)
        self.clients = []

    def register(self, client):
        if not client in self.clients:
            print("Registered client: {}".format(client))
            self.clients.append(client)

    def unregister(self, client):
        if client in self.clients:
            print("Unregistered client: {}".format(client))
            self.clients.remove(client)
        self._printConnected()

    def _printConnected(self):
        print("Connected clients:[")

    def notify_clients(self, message):
        print("Broadcasting: {}".format(message))
        for c in self.clients:
            c.sendFSEvent(message)
        print("\nSent messages")


if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python server_defer.py <dirs>")
        sys.exit(1)

    log.startLogging(sys.stdout)

    ffactory = WsServerFactory("ws://localhost", 8080)
    ffactory.protocol = WsProtocol
    listenWS(ffactory)

    observers = []
    for arg in sys.argv[1:]:
        dir_path = os.path.abspath(arg)
        if not os.path.exists(dir_path):
            print('{} does not exist.'.format(dir_path))
            sys.exit(1)
        if not os.path.isdir(dir_path):
            print('{} is not a directory.'.format(dir_path))
            sys.exit(1)

        # Check for and handle events
        event_handler = MessangerEventHandler(ffactory, reactor, os.getcwd())

        observer = Observer()
        observer.schedule(event_handler, path=dir_path, recursive=True)
        observer.start()

        observers.append(observer)

    try:
        reactor.run()
    except KeyboardInterrupt:
        for obs in observers:
            obs.stop()
        reactor.stop()
        print("\nGoodbye")
        sys.exit(1)

Any help would be greatly appreciated.

Thank you,

Brian

Upvotes: 3

Views: 2283

Answers (1)

notorious.no
notorious.no

Reputation: 5107

Most enterprise distros come with inotify which is really well suited for monitoring files and directories. The basic idea is to capture a list of connected web socket clients as they connect. Then create a callback that will execute when a change occurs on the files you're monitoring. Within this callback, you can iterate the clients and send them a message like 'file: "blah/blah.txt" has changed'. It's a little wonky, but the code snippet should clear things up for you.

from functools import partial
from twisted.internet import inotify
from twisted.python import filepath
# the rest of your imports ...


class SomeServerProtocol(WebSocketServerProtocol):
    def onConnect(self, request):
        self.factory.append(self)       # <== append this client to the list in the factory


def notification_callback(ignored, filepath, mask, ws_clients):
    """
    function that will execute when files are modified
    """
    payload = "event on {0}".format(filepath)
    for client in ws_clients:
        client.sendMessage(
            payload.encode('utf8'),    # <== don't forget to encode the str to bytes before sending!
            isBinary = False)

if __name__ == '__main__':
    root = File(".")
    factory = WebSocketServerFactory(u"ws://127.0.01:8080")
    factory.protocol = SomeServerProtocol
    factory.clients = []        # <== create a container for the clients that connect

    # inotify stuff
    notify = partial(notification_callback, ws_clients=factory.clients)   # <== use functools.partial to pass extra params
    notifier = inotify.INotify()
    notifier.startReading()
    notifier.watch(filepath.FilePath("/some/directory"), callbacks=[notify])

    # the rest of your code ...

Upvotes: 2

Related Questions