Search code examples
pythonwebsockettornadochatbot

Sending messages actively from Tornado WebSocket server


I have a Python WebSocket server. This can return a response when it receives a message.

import tornado.web
import tornado.websocket
import tornado.ioloop

class WebSocketHandler(tornado.websocket.WebSocketHandler):

    def open(self):
        print("New client connected")
        self.write_message("You are connected")

    def on_message(self, message):
        self.write_message(u"You said: " + message)

    def on_close(self):
        print("Client disconnected")

    def check_origin(self, origin):
        return True

application = tornado.web.Application([
        (r"/", WebSocketHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

However, this can't send messages before it receives one. How do I send a message actively? For example, it measures time and if it didn't receive messages for 10 sec, it send "Are you sleeping?".

I want to make chatbot using WebSocket. I use tornado and websocket because I only know this, and I would be interested if you know of better methods to use in this regard.


Solution

  • You can add a schedule with tornado.ioloop.PeriodicCallback and simply check if there has been activity or not based on time, like the following (I adapted from your code and this answer):

    import tornado.web
    import tornado.websocket
    import tornado.ioloop
    import time
    
    class WebSocketHandler(tornado.websocket.WebSocketHandler):
        def simple_init(self):
            self.last = time.time()
            self.stop = False
    
        def open(self):
            self.simple_init()
            print("New client connected")
            self.write_message("You are connected")
            self.loop = tornado.ioloop.PeriodicCallback(self.check_ten_seconds, 1000, io_loop=tornado.ioloop.IOLoop.instance())
            self.loop.start()
    
        def on_message(self, message):
            self.write_message(u"You said: " + message)
            self.last = time.time()
    
        def on_close(self):
            print("Client disconnected")
            self.loop.stop()
    
        def check_origin(self, origin):
            return True
    
        def check_ten_seconds(self):
            print("Just checking")
            if (time.time() - self.last > 10):
                self.write_message("You sleeping mate?")
                self.last = time.time()
    

    Now with a client alternating between idling and writing (adapted from here):

    class Client(object):
        def __init__(self, url, timeout):
            self.url = url
            self.timeout = timeout
            self.ioloop = IOLoop.instance()
            self.ws = None
            self.connect()
            PeriodicCallback(self.keep_alive, 20000, io_loop=self.ioloop).start()
            self.ioloop.start()
    
        @gen.coroutine
        def connect(self):
            print("trying to connect")
            try:
                self.ws = yield websocket_connect(self.url)
            except Exception as e:
                print("connection error")
            else:
                print("connected")
                self.run()
    
        @gen.coroutine
        def run(self):
            once = False
            while True:
                msg = yield self.ws.read_message()
                print(msg)
                if once:
                    time.sleep(11)
                    once = False
                else:
                    time.sleep(1)
                    once = True
                self.ws.write_message("Hello matey")
                if msg is None:
                    print("connection closed")
                    self.ws = None
                    break
    
        def keep_alive(self):
            if self.ws is None:
                self.connect()
            else:
                self.ws.write_message("keep alive")
    

    We obtain the expected output:

    trying to connect
    connected
    You are connected
    You said: Hello matey
    You sleeping mate?
    You said: Hello matey
    You said: Hello matey
    You sleeping mate?