how to flash messages emit by flask_socketio? - python

I can not use flask flash messages sent with emit and "captured" with #socketio.on, but it works on the html page. How to fix flash from flask_socketio ?
When i look in terminal, it works.
it's at the jonction socket / flash that the problem is .
$('form#send_room').submit(function(event) {
socket.emit('my_room_event', {room: $('#room_name').val(), data: $('#room_data').val()});
return false;
});
events.py
#socketio.on( 'send_room' , namespace='/roomy/roomy')
def broadcast_info_new_box(data):
print("\n\n\n broadcast_info_new_box called with socket on landing events.py")
flash(data)

The flash() function works only in Flask routes, as it relies on an HTTP response delivering an updated session cookie to the client. You are trying to use it in a Socket.IO event handler, which does not have a way to send cookies to the client.
If you want to implement alert popups or similar via Socket.IO you will have to emit these alerts as events to the client, and then use JavaScript in the client to display them.

Related

Sending message from Flask to JS via socketio

I'm trying to wrap my head around this socketio thing and have hit a wall regarding sending a message from my Flask backend to my JavaScript frontend. My app is a consolidated control panel for my church's PTZ camera and OBS Studio. Here are the logical steps I'm trying to implement:
Connect Flask to OBS via the OBS websocket.
Receive notification in Flask when the Graphics source visibility in OBS changes.
Send a message from Flask to my JS frontend to let it know that the Graphics source has changed visibility (making a two-way sync with the button on my control panel and the actual source in OBS).
Steps 1 and 2 are working, but I can't get the 3rd one to work.
Here is my Python where I use the message from OBS to call the function to sync with the JS frontend. (I know the sync_graphics_toggle runs, but the JS never gets the emit message.)
def on_visibility_change(message):
if (message.getItemName() == "Graphics"):
sync_graphics_toggle(message.getItemVisible())
def sync_graphics_toggle(obs_graphics_visible):
socketio.emit('syncGraphicsToggle', obs_graphics_visible, namespace='/main')
In the JavaScript (with just a console log to test):
$(document).ready(function() {
const socketMain = io("/main");
socketMain.on("connect", function() {
console.log("Made connection in graphics-preview", socketMain.id);
});
socketMain.on("syncGraphicsToggle", function(obsGraphicsVisible) {
console.log("Got syncGraphicsToggle command")
});
}
I have verified that I'm connected, but I don't get the message from Flask.
I have other implementations of socketio in my code that work fine, but those are initiated by actions on the webpage. So, I'm thinking there is something I don't understand about sending messages that are initiated by the server.
Any pointers would be greatly appreciated.
John
Windows 10
Python = 3.10.4
Flask = 2.2.2
Python Socket.IO = 5.7.2
JS Socket.IO = 4.5.3

flask socketio sends data to a reactjs component which has not yet been rendered

I have a flask socketio server wherein if the database is updated, it sends a ping to a flask server and it originates and sends the data to be displayed to certain ports and namespaces.
There are several react pages that use socket.io-client to fetch this data through a socket.on event
the problem is that a react page only renders when the page is opened and the socket connection from the client side for a certain namespace is only made from the componentDidMount
if the server sends data but the page is not rendered, will the data be displayed when I open it?

Put <Gather> result in variable in twilio.rest

So, i have this code:
import os
from twilio.rest import Client
xml=f'''
<Response>
<Say language="ru-RU">Здравствуйте, пожалуйста введите код для подтверждения.</Say>
</Response>'''
account_sid = ('AC274461ad47988c753424a3c8735dbcc1')
auth_token =('8ac88e8d5bce419ae3b5cbac4fc255f9')
client = Client(account_sid, auth_token)
call = client.calls.create( twiml=xml,
to='+375336412273',
from_='+12318247004',
)
print(call.sid)
I want to put in xml, that way, so i could put result of (what user typed in) in variable.
I want to do it only with python and twilio.rest, on twilio site i only found how to do it with flask, url and twiml.
Twilio developer evangelist here.
In order to be able to run interactive phone calls, there needs to be a way for your application to give instructions to Twilio, receive responses and then give further instructions. The instructions are the TwiML that you send to Twilio, but the way that Twilio communicates things back to you, like the result of what a user typed during a <Gather>, is via webhooks. Webhooks are HTTP requests from Twilio that include data about the call, like user input, in the body of the request.
To use that data and provide Twilio with further TwiML instructions your application needs to be able to receive HTTP requests and respond with an HTTP response containing the TwiML.
There are example in the Twilio documentation using Flask because Flask is a good way to receive and respond to HTTP requests in Python. You could build an application without a framework like Flask, but you would still need to be able to receive an incoming HTTP request and respond to it.

JavaScript EventSource triggers onerror after every received message

I have a web app utilizing Python Flask where I am trying to use Server Sent Events (SSEs) to push messages to web pages without having to poll from the client side or request that data. I'm using Redis to listen for new data which will then be sent to the web page. To start and make sure that I can use SSEs correctly, I've used a template similar to an example like this (How to implement server push in Flask framework?).
The problem I'm running into is that every time the client receives a message, the EventSource onmessage() method is called and delivers the message properly, but then the .onerror() method immediately gets triggered, causing the client to try to reconnect. This results in the '/listen' endpoint being called over and over and over, leading to the creation of many redis pubsub objects that are redundant and subscribe to the same channels.
The python code that runs the flask app is as follows
import flask
from flask_bootstrap import Bootstrap
from redis import Redis
from flask_wtf import FlaskForm
app = flask.Flask(__name__)
bootstrap = Bootstrap(app)
red = Redis(host='localhost', port=6379, db=0)
#app.route('/listen')
def listen():
pubsub = red.pubsub()
pubsub.subscribe('chat')
def stream():
for message in pubsub.listen():
if message['type'] == 'message':
msg = f"data: {message['data'].decode('utf-8')}\n\n"
yield msg
for msg in stream():
return Response(msg, mimetype='text/event-stream')
#app.route('/sse_page', methods=['GET', 'POST'])
def sse_page():
form = FlaskForm()
return render_template('sse_page.html', title='Server Push Testing', form=form)
if __name__ == "__main__":
app.run(port=8000, threaded=True, debug=True)
The corresponding section of sse_page.html where I try to open the EventSource and listen for the events stream is
<body>
<div id="target_div">Watch this space...</div>
</body>
<script>
var source = new EventSource("/listen");
source.onmessage = function (event) {
console.log('data: ', event)
$("#target_div").text(event.data)
};
source.onerror = function (event) {
console.log('error ', event)
};
source.onopen = function (event) {
console.log('open', event)
};
</script>
Using the redis-cli to send messages like those seen
here (and transcribed below)
127.0.0.1:6379> publish chat a
(integer) 1
127.0.0.1:6379> publish chat b
(integer) 2
Result in the console logging messages from Eventsource.onopen(), Eventsource.onmessage(), and Eventsource.onerror() for every single message, as seen
here.
I cannot figure out why the eventsource has an error after every single message that is received or how to prevent that from happening.
The answer to this question is NOT a problem in the code itself.
What this ended up being was an issue with the anti-virus security that was being used on the machine. Using Sophos AV Endpoint was causing each SSE to be treated as a download, and so any of the text data was unable to be streamed until the 'download was complete'.
This is (apparently) a known issue (see link https://community.sophos.com/on-premise-endpoint/f/sophos-endpoint-software/74878/server-sent-events-blocked-by-download-scanner) and there are a couple of ways to deal with it. You can either disable web scanning by Sophos (which does not work if you do not have administrator permissions) or run the flask app securely over HTTPS (https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https has a great tutorial).
Credit too should go to this post (JavaScript EventSource SSE not firing in browser), which is how I was able to find that the AV software was what ended up causing my issues.

Flask-Login + Flask-Sockets = Chaos

I have installed Flask-Login and Flask-Sockets. Both of them work fine HOWEVER when I try to get the current user (using g.user), I get an AttributeError: '_AppCtxGlobals' object has no attribute 'user'
#sockets.route('/echo')
def echo_socket(ws):
with app.app_context():
user = current_user #This causes problems, just like basically anything else that uses something that is not provided in this function
while True:
message = ws.receive()
ws.send("lol")
I am still new with Flask, any help would be super appreciated.
The problem is that you are treating your socket functions as if they were regular requests, which they are not.
In a regular (i.e. non-socket) situation the client sends requests to the server. Each request contains a cookie that was set at login time by Flask-Login. This cookie contains the user that is logged in. Flask-Login has a before_request handler that reads this cookie, loads the user and then exposes it through current_user, so that by the time your function runs you have access to this information.
Socket connections are nothing like the above. For a socket connection the client opens a socket to a route and that establishes a permanent connection with the server. The concept of a request does not exist here, it's just two machines sending and receiving data using their own protocol. Because there are no HTTP requests there is also no cookies, no before_request handlers, no application and request contexts pushed and no current_user from Flask-Login. The socket routes really function outside of the Flask application.
If you need to know the user you will need to have the client send credentials through the socket interface, and you will need to authenticate those credentials in your socket route on your own, since Flask-Login cannot work in the socket environment due to the lack of requests, cookies, etc. A significant difference between sockets and regular routes is that the socket connection is permanent, so you only need to authenticate the user when the connection is established.
I hope this helps. Sorry I don't have a quick and easy solution for you.

Categories

Resources