0

Background

The purpose of this project is to create a SMS based kill switch for a program I have running locally. The plan is to create web socket connection between the local program and an app hosted on Heroku. Using Twilio, receiving and SMS will trigger a POST request to this app. If it comes from a number on my whitelist, the application should send a command to the local program to shut down.

Problem

What can I do to find a reference to the namespace so that I can broadcast a message to all connected clients from a POST request?

Right now I am simply creating a new web socket client, connecting it and sending the message, because I can't seem to figure out how to get access to the namespace object in a way that I can call an emit or broadcast.

Server Code

from gevent import monkey
from flask import Flask, Response, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.mixins import BroadcastMixin
from time import time
import twilio.twiml
from socketIO_client import SocketIO #only necessary because of the hack solution
import socketIO_client 

monkey.patch_all()

application = Flask(__name__)
application.debug = True
application.config['PORT'] = 5000

# White list
callers = {
    "+15555555555": "John Smith"
}

# Part of 'hack' solution
stop_namespace = None
socketIO = None

# Part of 'hack' solution
def on_connect(*args):
    global stop_namespace
    stop_namespace = socketIO.define(StopNamespace, '/chat')

# Part of 'hack' solution
class StopNamespace(socketIO_client.BaseNamespace):

    def on_connect(self):
        self.emit("join", 'server@email.com')
        print '[Connected]'

class ChatNamespace(BaseNamespace, BroadcastMixin):
    
    stats = {
        "people" : []
    }

    def initialize(self):
        self.logger = application.logger
        self.log("Socketio session started")

    def log(self, message):
        self.logger.info("[{0}] {1}".format(self.socket.sessid, message))

    def report_stats(self):
        self.broadcast_event("stats",self.stats)

    def recv_connect(self):
        self.log("New connection")

    def recv_disconnect(self):
        self.log("Client disconnected")
        
        if self.session.has_key("email"):
            email = self.session['email']

            self.broadcast_event_not_me("debug", "%s left" % email)
            
            self.stats["people"] = filter(lambda e : e != email, self.stats["people"])
            self.report_stats()

    def on_join(self, email):
        self.log("%s joined chat" % email)
        self.session['email'] = email

        if not email in self.stats["people"]:
            self.stats["people"].append(email) 

        self.report_stats()

        return True, email

    def on_message(self, message):
        message_data = {
            "sender" : self.session["email"],
            "content" : message,
            "sent" : time()*1000 #ms
        }
        self.broadcast_event_not_me("message",{ "sender" : self.session["email"], "content" : message})
        return True, message_data

@application.route('/stop', methods=['GET', 'POST'])
def stop():
'''Right here SHOULD simply be Namespace.broadcast("stop") or something.'''
    global socketIO
    if socketIO == None or not socketIO.connected:
         socketIO = SocketIO('http://0.0.0.0:5000')
         socketIO.on('connect', on_connect)
     global stop_namespace
     if stop_namespace == None:
         stop_namespace = socketIO.define(StopNamespace, '/chat')
         stop_namespace.emit("join", 'server@bayhill.com')
     stop_namespace.emit('message', 'STOP')
     return "Stop being processed."

@application.route('/', methods=['GET'])
def landing():
    return "This is Stop App"

@application.route('/socket.io/<path:remaining>')
def socketio(remaining):
    try:
        socketio_manage(request.environ, {'/chat': ChatNamespace}, request)
    except:
        application.logger.error("Exception while handling socketio connection",
                         exc_info=True)
    return Response()

I borrowed code heavily from this project chatzilla which is admittedly pretty different because I am not really working with a browser.

Perhaps Socketio was a bad choice for web sockets and I should have used Tornado, but this seemed like it would work well and this set up helped me easily separate the REST and web socket pieces

Community
  • 1
  • 1
Bryant Wolf
  • 11
  • 1
  • 2

1 Answers1

3

I just use Flask-SocketIO for that.

from gevent import monkey
monkey.patch_all()

from flask import Flask
from flask.ext.socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/trigger')
def trigger():
    socketio.emit('response',
        {'data': 'someone triggered me'},
        namespace='/global')
    return 'message sent via websocket'

if __name__ == '__main__':
    socketio.run(app)