Private chat with flask-socketio - python

I'm developing a webapp for a personal assistant. While a user is connected to the site, I want them to be able to subscribe to their personal notifications while they're online. I went about doing this with socketio and flask socketio and I thought that I could just use multithreading, like so:
def update_loop():
while my_update_condition:
if my_update_exists:
socketio.emit("update", my_update)
#socketio.on("get_updates")
def get_updates(data):
'''Websocket thread for getting updates'''
socketio.emit("debug", {"value": "Starting update loop"})
update_thread = threading.Thread(target=update_loop)
update_thread.start()
But my code using this strategy gives any update to all users online. Using flask socketio, how can I securely implement a private chat? The data in the updates isn't ultra-sensitive, but, since it's notifications that the user sets, it's usually personal. Thanks.
Note: In my research on this I came upon something using a socketid to send a message just to a specific sender, but I couldn't find an example implementation of this using flask-socketio.

The socketio.emit() function broadcasts to all users by default. To address a message to a single user, you have to set the room to the desired user's room, which is named after the session id of the user. Here is an example:
def update_loop(sid):
while my_update_condition:
if my_update_exists:
socketio.emit("update", my_update, room=sid)
#socketio.on("get_updates")
def get_updates(data):
'''Websocket thread for getting updates'''
socketio.emit("debug", {"value": "Starting update loop"})
update_thread = threading.Thread(target=update_loop, args=(request.sid,))
update_thread.start()

Related

Flask POST Request to start an asynchronous process/task (or use websockets)

I have a Flask endpoint that starts an automated email campaign to a list of contacts I have. The body of the POST request has a "emailAmount" key which tells the process how many emails to send out.
Essentially what I want it to do is start the process of sending these emails out, and after each email sent out, show live feedback on the frontend like a loading bar, or just a live count of how many emails have been sent since the process started, and then some sort of live feedback on the front end telling the user when the process has completed. The email process is abstracted to a class I built called "AutomateEmailClient".
from flask import Blueprint
from flask_login import login_required, current_user
from forms.startProcess import ProcessForm
from ..emails.client import AutomateEmailClient
email_router = Blueprint("email",__name__)
#email_router.route('/start', methods=['POST'])
#login__required()
def start_process():
form = ProcessForm() #Validate body of req with WTForms
if form.validate_on_submit():
#Interact with AutomateEmailClient object from different package a few levels up
return #? Return synchronously after email process has finished? or asynchronously and use websockets for live feedback to frontend?
However, after I return from the start_process() function, obviously the request/response cycle is dead, and if I just run the email process and have the return synchronously after, I would basically just have a hanging response for several minutes.
I'm assuming I need to use websockets, and possibly some sort of asynchronous functionality or multi processing, but I'm not sure what is the best approach or how to implement it.

Slack API Events are not registering in our application

Hello Fellow Slack Bot Enthusiasts.
It's my first time to start setting up a Slack Bot (and I don't have a lot of dev experience, so I wanted to inquire!) .
For this, I used python and Slack bolt.
Background
I was trying to setup my Amazon EC2 Web Instance's Load Balancer to accept slack events from my Workspace. In the below photo, I am now able to have my Endpoint URL Verified in the Slack bot.
Next, I am trying to follow the instructions listed in the Slack Bolt homepage that told me to create the app.py test file. So I followed through on the instructions and ensured that I subscribed to below events:
app_home_opened
message.channels
message.groups
message.im
Then I created the sample app.py file from the article, that was supposed to handle the app_home_opened event.
Now I tried to run the application in my command line (I added the section for #app.event(app_home_opened), and all the other required events ), and I tried to trigger the app_home_opened, message.channels, message.im events, by opening the home page of my bot and by dm-ing the bot and inviting it to a channel and trying to talk to it - but I don't seem to be receiving any payload in the back-end.
I wanted to inquire about the "Verified" notification from the Slack Bot. Does this ensure that the connection between my chatbot code and the server are actually linked?
In addition, I wanted to inquire about ways to test this so that I can ensure that the connection is actually working as expected.
If you could share some thoughts about what I can do to test the communication, it would be much appreciated. Thank you!
Summary
TLDR:
Problem: My chatbot is not receiving any payload from Slack.
Expected: I think there should be some interaction saying HTTP / 200 response to indicate that it is OK, etc.
Actual: The chatbot just remains the same saying "⚡Bolt App is Running"
What I've tried:
Reinstalling the application to ensure that all my changes were saved and were reflected properly in my Slack Environment
Testing the communication by curling through to the URL (it responded with challenge parameter), so I thought that it was OK
Testing the communication by entering some text via DM / channel communication, and opening the homepage.
Sample Code:
import os
# Use the package we installed
from slack_bolt import App
# Initializes your app with your bot token and signing secret
app = App(
token=os.environ.get("SLACK_BOT_TOKEN"),
signing_secret=os.environ.get("SLACK_SIGNING_SECRET")
)
# Add functionality here
# #app.event("app_home_opened") ...
# added some of the code from the guide here and the other #app.events ("")
# Start your app
if __name__ == "__main__":
app.start(port=int(os.environ.get("PORT", 3000)))
slack_bolt, for some reason that escapes me, does not seem to create the event handler endpoint (/slack/events) and instead seems to prefer running the application in socket mode. It may still be worth looking into socket mode (see SocketModeHandler) but this example from slack_bolt's github repo ended up getting me to my solution: https://github.com/slackapi/bolt-python/blob/main/examples/flask/app.py
The short-ish version is you have to utilize slack_bolts's flask adapter to create a SlackRequestHandler then create a flask app, define the slack events endpoint (/slack/events) for the flask app, and pass the result to the bolt SlackRequestHandler (Events Endpoint -> Flask -> SlackRequestHandler -> Bolt):
import os
from flask import Flask, request
from slack_bolt import App
from slack_bolt.adapter.flask import SlackRequestHandler
bolt_app = App(
token=os.environ.get("SLACK_BOT_TOKEN"),
signing_secret=os.environ.get("SLACK_SIGNING_SECRET")
)
flask_app = Flask(__name__)
handler = SlackRequestHandler(bolt_app)
# Add functionality here
# #bolt_app.event("app_home_opened") ...
# added some of the code from the guide here and the other #bolt_app.events ("")
#flask_app.route("/slack/events", methods=["POST"])
def slack_events():
return handler.handle(request)
if __name__ == "__main__":
flask_app.run()
Hope this helps, cheers!

How to store a message from the client in the server and send it further via some route in Flask

From a web application, this is making a request to the backend application (Python with Flask and flask-socketio). From this route on the backend, an emit should be done to a socketio client standalone application. This works fine, but when the client app sends back a message directly after, I want to retrieve this message and send it back in my route to the web application. The message I get back from the client via a callback will be asynchronous, so how in the simplest manner could this be achieved? Each time I fetch the message from the client, the route has already sent back a reply to the web app without the message.
I fully understand that this flow is usually not normal, but can this be achieved without saving this message into a database, but store it somewhere on the backend and send it back to the web app?
You can use an Event object from the Python standard library.
from threading import Event
my_event = Event()
In your Flask route:
my_event.wait() # block until the event is signaled
return socketio_response
In your Socket.IO callback function:
socketio_response = data
my_event.set() # alert the route that a result is now available

How to use python-slackclient RTM using multiple token (bot-user)

Hi I have an issue integrating slack custom bot-user into my slack app, based on python-slackclient documentation python-slackclient
to use the RTM
import time
from slackclient import SlackClient
token = "xoxp-xxxxxxxxx"# found at https://api.slack.com/web#authentication
sc = SlackClient(token)
if sc.rtm_connect():
while True:
print sc.rtm_read()
time.sleep(1)
else:
print "Connection Failed, invalid token?"
that code is working for bot-user token, but since I use oauth, I need to connect RTM using the bot_access_token everytime user install my app to act on behalf my app to the added team
any solution or example how to do it?
Cheers,
Your question is had to understand. You wrote:
since I use oauth, I need to connect RTM using the bot_access_token everytime user install my app to act on behalf my app to the added team
The access token you're using here...
token = "xoxp-xxxxxxxxx"# found at https://api.slack.com/web#authentication
...should be the same as the access token that is associated with your bot. (You should not make your bot use your own personal access token!) You can get an access token for your bot at https://my.slack.com/services/new/bot (assuming you're logged into Slack in the browser with which you follow that link).
If you participate in multiple Slack "teams" (a Slack "team" being basically a company), you'll need to set up a separate bot for each "team". Each bot will have a different access token. To pass the correct access token in to your bot, you could add a command-line parameter, or read the token from an environment variable, or read it from disk, among other options.
You can loop over tokens for connecting if you are planning to setup bot
for multiple teams, then your code can be converted to :-
clients = [SlackClient(token) for t in tokens]
for client in clients:
client.rtm_connect()
while True:
for client in clients:
print client.rtm_read()
time.sleep(1)

Google Authentication using Tornado Web Framework(Python)

I am using the Tornado Web Framework for its's asynchronous call back facilities, and trying to setup a Google authentication using OAuth in Tornado.
Currently the authentication goes to the step where it ask for my permissions to access the data and then a No Data Received screen comes up as it is not able to exchange the access token with my application.
can you update your question with some code ?? i can update my answer based on the error or the code you update.
if you want to refer to some docs u can look at http://tornado.readthedocs.org/en/latest/auth.html
below is an example code which would help you.
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
tornado.auth.GoogleOAuth2Mixin):
#tornado.gen.coroutine
def get(self):
if self.get_argument('code', False):
user = yield self.get_authenticated_user(
redirect_uri='http://your.site.com/auth/google',
code=self.get_argument('code'))
# Save the user with e.g. set_secure_cookie
else:
yield self.authorize_redirect(
redirect_uri='http://your.site.com/auth/google',
client_id=self.settings['google_oauth']['key'],
scope=['profile', 'email'],
response_type='code',
extra_params={'approval_prompt': 'auto'})

Categories