Session variable value is not updated when websocket connection happens - Python - python

I am creating an API to send message to echo server
For this, I am using echo chat server URL ws://echo.websocket.org/ from this site https://www.websocket.org/echo.html. This connection will echo our input.
First time when the user request the API, I need to establish connection with echo server and send the user message to echo server. When second time user request the same API, this time connection is already established. So, I just need to send user message to echo server.
For this, I am using python session to store the connection details. First time when the connection in made I am trying to save it in session. session['ConnetionMade'] = "True" by default it is false
So, second time when the user request the API, this time ConnetionMade is True. So, I don't to make connection again.
But here session variable is not updated when the connection is made. It is always false. But we set to True.
Below is the full working code. Kindly help me to update session variable.
Note : Session variable works when we skips socket connection code
from flask import Flask
from flask import request, session
from config import config
import websocket
try:
import thread
except ImportError:
import _thread as thread
SECRET_KEY = 'a secret key'
app = Flask(__name__)
app.config.from_object(__name__)
#app.route('/')
def root():
return 'Hello NLP....!'
userMessage = ""
#app.route('/whatsapp', methods=['POST'])
def echo():
global userMessage
userMessage = request.json['message']
# session.clear()
print("\n\nuserMessage: ", userMessage, "\n\n")
print("ConnetionMade--: ", session.get('ConnetionMade', "False"))
if session.get('ConnetionMade', "False") == "False":
session['ConnetionMade'] = "True"
print('True set to ConnetionMade ', session.get('ConnetionMade', "False"))
echoConnection()
else:
session['ConnetionMade'] = "False"
print('False set to ConnetionMade ', session.get('ConnetionMade', "False"))
return ""
def echoConnection():
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.run_forever()
return ""
def on_message(ws, message):
print("\n\nMessage received from Echo Socket server:", message, '\n\n')
return
def on_error(ws, error):
print("on_error ", error)
return
def on_close(ws):
print("on_close")
return
def on_open(ws):
def run(*args):
print("\n\nSocket connection made. Now sending this message ("+userMessage+") to Echo Socket server\n\n")
ws.send(userMessage)
print("\nsent...\n")
print("thread terminating...")
thread.start_new_thread(run, ())
return
if __name__ == "__main__":
app.run(host='0.0.0.0', port=config['server']['port'])

In your case, the ws.run_forever() blocks the thread and then the further call's to the API doesn't get captured.
You can run the websocket in a deamon thread and ensure to use its send method to communicate with the websocket server.
Something like this:
from flask import Flask
from flask import request, session
import websocket
import threading
SECRET_KEY = 'a secret key'
app = Flask(__name__)
app.config.from_object(__name__)
websocket_client = None
#app.route('/')
def root():
return 'Hello NLP....!'
#app.route('/whatsapp', methods=['POST'])
def echo():
userMessage = request.json['message']
# session.clear()
print("userMessage: ", userMessage, "\n")
print("ConnetionMade--: ", session.get('ConnetionMade', "False"))
if session.get('ConnetionMade', "False") == "False":
session['ConnetionMade'] = "True"
print('True set to ConnetionMade ', session.get('ConnetionMade', "False"))
send_message_to_websocket_server(userMessage)
return ""
def send_message_to_websocket_server(message):
print("Sending message to WebSocket Server")
websocket_client.send(message)
def createConnection():
ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
# Initialise the run_forever inside a thread and make this thread as a daemon thread
wst = threading.Thread(target=ws.run_forever)
wst.daemon = True
wst.start()
return ws
def on_message(ws, message):
print("Message received from Echo Socket server:", message)
def on_error(ws, error):
print("on_error ", error)
def on_close(ws):
print("on_close")
def on_open(ws):
print("Socket connection opened")
if __name__ == "__main__":
websocket_client = createConnection()
app.run(host='0.0.0.0', port=8001) # I have hardcoded the port to test this
Now, using Curl if I take session into account and hit it with the following command, it only sends an echo to the websocket server if the session is new:
curl -s -i -X POST http://0.0.0.0:8001/whatsapp\
-d '{"message":"Sample"}' -H "Content-Type: application/json"\
--cookie cookie.txt --cookie-jar cookie.txt
It will always take the latest message and echo it to the console.

Related

Flask Socket-IO Server Client Not Communicating (with tweepy and twilio)

I am trying to have a Flask Server that allows me to launch a tweepy stream and on every message received in the stream listener, it sends that message to a socketio client. The Flask server is at the same time supposed to allow Twilio to post to it, and route that message to the client—so that the client is receiving messages from both Twilio and twitter.
I have been trying to get the server to send messages over to the client for the data incoming from twitter, the code for Twilio works just fine. It sends data over to the client on message receipt. The main loop in tweepy is also not locking up the program—I can test print statements and see tweets and the incoming sms's being printed in the handle_message(msg) function asynchronously. I feel like there must be something really simple that I am missing here since the SMS's are emitted to the client, but the incoming tweets are not, even though they are propagating through to the handle_message(msg) function. What gives?
server.py
from flask import Flask, json, request
from twilio.twiml.messaging_response import Message, MessagingResponse
from flask_socketio import SocketIO
import tweepy
import json
PATH = '/path/to/credentials/'
with open(PATH, "r") as file:
credentials = json.load(file)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'abc123'
sio = SocketIO(app, cors_allowed_origins="*")
auth = tweepy.OAuthHandler(credentials['CONSUMER_KEY'], credentials['CONSUMER_SECRET'])
auth.set_access_token(credentials['ACCESS_TOKEN'], credentials['ACCESS_SECRET'])
api = tweepy.API(auth)
class MyListener(tweepy.StreamListener):
def on_status(self, status):
print('status')
def on_data(self, data):
handle_message(data)
def on_error(self, status):
print('error')
print(status)
stream_listener = MyListener()
# twilio sms route
#app.route('/sms', methods=['POST'])
def sms():
number = request.form['From']
message_body = request.form['Body']
message_data = {"number": number, "msg": message_body}
resp = MessagingResponse()
resp.message('Hello {}, you said: {}'.format(number, message_body))
handle_message(message_data)
return str(resp)
# flask-socketio stuff
#sio.on('connect')
def connect():
print('connected')
sio.emit('client_connected', "you connected")
search_term = "#mysearchterm"
stream = tweepy.Stream(auth=api.auth, listener=stream_listener)
stream.filter(track=[search_term], is_async=True)
sio.emit('client_connected', "the search term is {}".format(search_term))
#sio.on('disconnect')
def disconnect():
print('Client Diconnected')
#sio.event
def handle_message(message):
print("This is the message received: ", message)
sio.emit('handle_message', message)
if __name__ == '__main__':
sio.run(app)
client.py
import socketio
client = socketio.Client()
#client.on('client_connected')
def on_connect(message):
print(message)
#client.on('handle_message')
def message(data):
print(data)
client.connect('http://localhost:5000/')
Twilio developer evangelist here.
You've decorated the handle_message function as #sio.event but as far as I can see in the docs, you should only do that to have the handle_message method respond to events on the socket called "handle_message".
I'd start be removing the #sio.event decorator.
I'm not a Python expert, but I also wonder whether there is a scope issue here. You define your MyListener class and create an instance of it before you define the handle_message method. Just to test, can you try emitting to the socket directly within the on_data method:
def on_data(self, data):
sio.emit('handle_message', data)
If that works, consider moving the definition of handle_message above the definition of MyListener.
I solved my problem! As I noted in this comment, the issue was with multithreading and passing information between the threads. With tweepy, the parameter is_async=True, which in 4.1.0 is threading=True, opens up a new thread once the stream is run.
Instead of trying to deal with passing information around, I exploited the extant flask-socketio functionality by using a local redis server as a message queue (start from the section "Using Multiple Workers" if you are setting this up for the first time, also be sure to install redis).
Here is the updated server.py code. The client.py code remained essentially unchanged:
import eventlet
eventlet.monkey_patch()
from flask import Flask, json, request
from twilio.twiml.messaging_response import Message, MessagingResponse
from flask_socketio import SocketIO
import tweepy
import json
PATH = '/PATH/TO/CREDENTIALS'
with open(PATH, "r") as file:
credentials = json.load(file)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'abc123'
sio = SocketIO(app, message_queue='redis://', cors_allowed_origins="*")
class MyStream(tweepy.Stream):
def __init__(self, consumer_key, consumer_secret, access_token, access_secret):
super(MyStream, self).__init__(consumer_key, consumer_secret, access_token, access_secret)
self.stream_sio = SocketIO(message_queue='redis://')
def on_status(self, status):
print('status')
def on_data(self, data):
json_data = json.loads(data)
self.stream_sio.emit('handle_message', json_data['text'])
# TODO: Send along all necessary information
#app.route('/sms', methods=['POST'])
def sms():
number = request.form['From']
message_body = request.form['Body']
message_data = {"number": number, "msg": message_body}
resp = MessagingResponse()
resp.message('Hello {}, you said: {}'.format(number, message_body))
handle_message(message_data)
return str(resp)
#sio.on('connect')
def connect():
print('connected')
sio.emit('client_connected', "you connected")
search_term = "#testingtesting123"
stream = MyStream(credentials['CONSUMER_KEY'], credentials['CONSUMER_SECRET'],
credentials['ACCESS_TOKEN'], credentials['ACCESS_SECRET'])
stream.filter(track=[search_term], threaded=True)
sio.emit('client_connected', "the search term is {}".format(search_term))
#sio.on('disconnect')
def disconnect():
print('Client disconnected')
def handle_message(message):
sio.emit('handle_message', message)
if __name__ == '__main__':
sio.run(app)

mqtt client does not receive message in case of thread and rest-api

I have a python script, which based on flask and mqtt. The use case is to receive a request via rest-api then to create a new thread which publishes some messages on mosquitto mqtt and expects a response (see subscribe). My problem is that I don't receive any messages. I think it has something to do with the thread, because without the thread it's working very fine..
Do you know what can be the problem?
Thank you in anticipation!
here the code:
from flask import Flask, Response
import paho.mqtt.client as mqtt
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("/mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
print(msg.topic)
client = mqtt.Client(client_id=client_name, clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
client.username_pw_set(mqtt_user, mqtt_password)
client.loop_start()
client.connect(mqtt_host)
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("/mytopic", "")
while True:
check the response from mqtt => but i don't get the response anymore
....
break
finally:
lock.release()
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
app.run(debug=True)
There are a couple of problems with this.
Both the client.connect() and the client.subscribe() calls need iterations of the client network loop to run in order to complete properly.
The network loop needs to run at least once every keep alive period the time after the connection has been made in order to stop the broker disconnecting the client as dead. This means if there is a delay between starting the code and the first REST request then the client will be disconnected.
Better to use the client.start_loop() function to run MQTT client network loop continuously in the background on it's own.
You should also remove the call to client.subscribe() that is outside the on_connect() callback.
EDIT:
As hashed out in the comments/chat the following works. It looks like running the flask app in debug mode does some strange things and creates multiple MQTT clients over and over again with the same client id. This leads to the broker constantly kicking the old ones off so messages never get delivered.
from flask import Flask, Response
import paho.mqtt.client as mqtt
import time
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
sessionId=0
cont=True
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
global cont
print(msg.topic)
cont=False
client = mqtt.Client(client_id="foo", clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
#client.username_pw_set(mqtt_user, mqtt_password)
client.connect("localhost", port=1884)
client.loop_start()
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("mytopic", "foo")
while cont:
time.sleep(5)
print("loop")
finally:
lock.release()
result = "foo"
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
print("started")
app.run()

Python Tornado: Sending websocket messages from another class

Have an app that is using (Python 3.6) Tkinter & Tornado. Would like it send a websocket message when a button is pressed.
The sendSocket is in my class that handles the interface. I am able to open my sockets ok, and can send data into the socket handler ok. Additionally, it serves up my html file ok from my RequestHandler.
I can see that my code hits the sendSocketMessage line ok. However, I never get the print from within the SocketHandler.send_message def. There are no errors in the console.
def sendSocketMessage(self, data = "whatever"):
print("sending")
#WebSocketeer.send_message(data)
ioloop.IOLoop.current().add_callback(WebSocketeer.send_message, data)
class WebSocketeer(websocket.WebSocketHandler):
def open(self):
print("WebSocket opened")
def on_message(self, message):
print("got message: " + message)
def on_close(self):
print("WebSocket closed")
#classmethod
def send_message(self, message):
print("sending message: " + message)
for session_id, session in self.session.server._sessions._items.iteritems():
session.conn.emit(event, message)
Code based off of these SO responses
Send a websocket message:
How do I send a websocket message in Tornado at will?
Send to all clients:
Is it possible to send a message to all active WebSocket connections? Using either node.js or python tornado websockets
Found a way to make it work here: How to run functions outside websocket loop in python (tornado)
But am still wondering why the add_callback doesn't work - as, from what I've read, this is the recommended way.
This is what I've got working, taken from: https://github.com/tornadoweb/tornado/issues/2802
clients = [];
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('connection opened...')
clients.append(self);
def on_message(self, message):
self.write_message("The server says: " + message + " back at you")
print('received:', message)
def on_close(self):
clients.remove(self);
print('connection closed...')
#classmethod
def send_message(self, message):
print("sending message: " + message)
for client in clients:
client.write_message(message);
#for session_id, session in self.session.server._sessions._items.iteritems():
# session.conn.emit(event, message);
return True;
def sendRandom():
global thread, data;
try:
print("sendRandom()");
time.sleep(0.125);
n = random.randint(0,1000);
data = str(n);
data = {"msg":"data","data":data};
if eventLoop is not None:
#If response needed
#sendData(eventLoop,WSHandler.send_message,json.dumps(data));
#else
eventLoop.add_callback(WSHandler.send_message,json.dumps(data));
except:
print("Err");
traceback.print_exc();
clients = [];
def sendData(loop,f,*a,**kw):
print("%s %s" % (type(loop),type(f)));
concurrent_future = concurrent.futures.Future();
async def wrapper():
try:
rslt = f(*a,**kw);
except Exception as e:
concurrent_future.set_exception(e);
else:
concurrent_future.set_result(rslt);
loop.add_callback(wrapper);
return concurrent_future.result();
eventLoop = None;
application = tornado.web.Application([
(r'/data', WSHandler),
])
def startServer():
global eventLoop;
try:
print("Starting server #%s:%d" %("localhost",9090));
asyncio.set_event_loop(asyncio.new_event_loop());
eventLoop = tornado.ioloop.IOLoop();
application.listen(9090)
eventLoop.start();
except KeyboardInterrupt:
print("^C");
except:
print("ERR");
traceback.print_exc();
if __name__ == "__main__":
thread = Thread(target=startServer,);
thread.setDaemon(True);
thread.start();
time.sleep(5);
while True:
sendRandom();

Tornado - Send message to client on demand

I'm using Tornado to serve a html file and, at the same time, I want my Python program to dynamically change a value in the same page through websockets. The purpose is that the value can be seen changing, without the need to refresh on the client side.
In this case, I want to send a message with a variable that changes from "1234" to "4321" every 4 seconds.
I have a thread doing the value toggling and I want that same thread to send that value to the websocket, so I can handle it with a script using the onmessage function in the client side.
The following code will give me the following error: 'Application' object has no attribute 'write_message'
import tornado.ioloop
import tornado.web
import tornado.websocket
import threading
import time
flag = True
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("mypage.html")
class wshandler(tornado.websocket.WebSocketHandler):
def open(self):
self.write_message('connected')
print "WebSocket opened"
def on_message(self, message):
self.write_message("You have sent: " + message)
def on_close(self):
print "WebSocket closed"
def write_message(self, message):
print ("writing message", message)
self.write_message(message)
application = tornado.web.Application(
handlers=[
(r"/", MainHandler),
(r"/ws", wshandler),
])
def toggle():
global flag
while True:
if flag==True:
print 'now on 1234'
flag = False
application.write_message('1234')
time.sleep(4)
elif flag==False:
flag = True
application.write_message('4321')
print 'Now on 4321'
time.sleep(4)
def main():
global response
valueThread=threading.Thread(target=toggle, name="Toggle Value Thread")
valueThread.setDaemon(True)
valueThread.start()
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
My problem is that every single tutorial/guide I've seen about Tornado only uses the function write_message within the open and onmessage function within the websocket class.. so I don't really get why this won't work.
You should only use the function write_message within the open and onmessage function within the websocket class. You are using application, which is a tornado.web.Application. It certainly won't have the method of tornado.websocket.WebSocketHandler. They are not the same class. Hence the error.
I would do something like:
class wshandler(tornado.websocket.WebSocketHandler):
def toggle(self):
flag = True
while True:
if flag==True:
print 'now on 1234'
flag = False
self.write_message('1234')
time.sleep(4)
elif flag==False:
flag = True
self.write_message('4321')
print 'Now on 4321'
time.sleep(4)
def on_message(self, message):
if message == 'toggle':
self.toggle()

Python client keeps websocket open and reacts to received messages

I'm trying to implement a REST client in python that reacts to messages received from the server received through an opened websocket with the concerned server.
Here is the scenario:
client opens a websocket with the server
from time to time, the server sends a message to the client
when the client receives the messages, it gets some information from
the server
The current client I have is able to open the websocket and to receive the message from the server. However, as soon as it receives the messages, it gets the information from the server then terminates while I'd like to keep it listening for other messages that will make it get a new content from the server.
Here is the piece of code I have:
def openWs(serverIp, serverPort):
##ws url setting
wsUrl = "ws://"+serverIp+":"+serverPort+"/websocket"
##open ws
ws = create_connection(wsUrl)
##send user id
print "Sending User ID..."
ws.send("user_1")
print "Sent"
##receiving data on ws
print "Receiving..."
result = ws.recv()
##getting new content
getUrl = "http://"+serverIp+":"+serverPort+"/"+result+"/entries"
getRest(getUrl)
I don't know if using threads is appropriate or not, I'm not expert in that.
If someone could help, it'll be great.
Thanks in advance.
I finished with this code, doing what I'm expecting. Git it from here
import websocket
import thread
import time
def on_message(ws, message):
print message
def on_error(ws, error):
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print "thread terminating..."
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://localhost:5000/chat",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()

Categories