How to run two threads ? So that the RunBot function is async? - flask

How to run two threads ? So that the RunBot function is async?
After startup, it works fine - but #client.event does not work due to the fact that the web server thread took
`
import discord
from ast import literal_eval
import aiohttp
import aiosqlite
from quart import Quart, render_template, request, session, redirect, url_for, make_response, websocket
from quart_discord import DiscordOAuth2Session, requires_authorization, Unauthorized
import asyncio
from threading import Thread
import multiprocessing as mp
TOKEN = "token"
client = discord.Client(command_prefix='-=-=-=', intents=discord.Intents.all())
app = Quart(__name__)
#client.event
async def on_ready():
print(f'{client.user} Bot Content')
#app.before_serving
async def before_serving():
async def RunBot()
#don't' work
#create new Thread
await client.run(True)
# loop = asyncio.get_event_loop()
# await client.login(TOKEN)
# loop.create_task(client.connect())
#client.event
async def on_message(message):
print(f'New msg {message.content}, Server: {message.guild}')
#app.route("/")
async def index():
return render_template('index.html')
if __name__ == "__main__":
from hypercorn.config import Config
from hypercorn.asyncio import serve
asyncio.run(serve(app, Config()))
`
I tried a simple launch via task but it didn't work
It is necessary that the web server does not block the client stream

Related

How do I start/stop Hypercorn/Uvicorn server as a background task for an async application (like a discord bot) Python

I am currently creating a django application w/ asgi and I am currently having problems of setting up hypercorn and uvicorn to run in background with graceful shutdown. When I set up my application from asgi to run on hypercorn only using asyncio.create_task and starting it only, the website doesn't run.
Hypercorn code snippet:
from scripts import funcs
import nextcord
from nextcord.ext import commands
from nextcord import Interaction
import asyncio
# from uvicorn import Config, Server
# import uvicorn
import subprocess
from subprocess import CREATE_NEW_CONSOLE
import signal
# import multiprocessing
import nest_asyncio
import os
import sys
sys.path.insert(1, 'C:\\Users\\Sub01\\Project\\PaulWebsite\\app')
from hypercorn.config import Config
from hypercorn.asyncio import serve
from hypercorn.run import run
import hypercorn
import asyncio
from paul_site.asgi import application
import signal
nest_asyncio.apply()
createEmbed = funcs.embedCreator()
shutdown_event = asyncio.Event()
def _signal_handler(*_) -> None:
shutdown_event.set()
class HYPERCORN:
config = Config()
coro = None
def __init__(self) -> None:
self.config.from_object("paul_site.asgi")
self.evtLoop = asyncio.new_event_loop()
async def start(self):
self.coro = self.evtLoop.create_task(await serve(application, self.config))
def stop(self):
self.evtLoop.add_signal_handler(signal.SIGINT, _signal_handler)
self.evtLoop.run_until_complete(
asyncio.to_thread(serve(application, self.config, shutdown_trigger=shutdown_event.wait))
)
class baseCommand(commands.Cog):
proc = None
def __init__(self, client):
self.client = client
self.website = HYPERCORN()
#nextcord.slash_command()
async def bot(self, interaction: Interaction):
pass
#bot.subcommand(description="Stops the bot")
async def shutdown(self, interaction: Interaction):
await interaction.response.send_message(embed=createEmbed.createEmbed(title="Exit", description="Bot's down", footer=f"Requested by {interaction.user.name}"))
exit()
# Create command group site
#nextcord.slash_command()
async def site(self, interaction: Interaction):
pass
#site.subcommand(description="Starts the website")
async def start(self, interaction: Interaction):
try:
await self.website.start()
await interaction.response.send_message(embed=createEmbed.createEmbed(title="Start Website", description=f"""
**Website started successfully**
""", footer=f"Requested by {interaction.user.name}"))
except Exception as e:
await interaction.response.send_message(
embed=createEmbed.createEmbed(title="Start Website Error", description=
f"""
```bash
{e}
```
""", footer=f"Requested by {interaction.user.name}")
)
#site.subcommand(description='Stops the website')
async def stop(self, interaction: Interaction):
self.website.stop()
await interaction.followup.send(embed=createEmbed.createEmbed(title="Stop Website", description=f"""
**Website stopped successfully!**
""", footer=f"Requested by {interaction.user.name}"))
del self.proc
def setup(client):
client.add_cog(baseCommand(client))
Uvicorn code snippet:
import sys
sys.path.insert(1, 'C:\\Users\\Sub01\\Project\\PaulWebsite\\app')
import asyncio
from paul_site.asgi import application
import signal
import time
import uvicorn
from multiprocessing import Process
class UvicornServer(uvicorn.Server):
def __init__(self, host: str = "127.0.0.1", port: int = 8000):
self.host = host
self.port = port
async def setup(self):
self.proc = Process(
target=uvicorn.run,
args=[application],
kwargs={
'host': self.host,
'port': self.port,
},
daemon=True
)
# self.proc.run()
await self.proc.start()
await asyncio.sleep(0.5)
async def down(self):
self.proc.terminate()
def blockingFunc():
prevTime = time.time()
while True:
print("Elapsed time: ", time.time() - prevTime)
time.sleep(1)
if time.time() - prevTime >= 4:
break
async def main():
server = UvicornServer()
await server.setup()
blockingFunc()
await server.down()
asyncio.run(main())
Asgi.py:
"""
ASGI config for paul_site project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
"""
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from paul_site_app.ws_urlpatterns import ws_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'paul_site.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': URLRouter(ws_urlpatterns)
})
Looking at the examples from people incorporating FastAPI and running uvicorn as a background task, I tried it but it only results in a runtime error. I've also tried having a command open a terminal and running the application via cli but soon realized that the code that invokes a new terminal isn't compatible with different platforms.

Best practices for authenticating Django Channels

Django 4.1.4
djoser 2.1.0
channels 4.0.0
I have followed the documented recommendation for creating custom middleware to authenticate a user when using channels and I am successfully getting the user
and checking that the user is authenticated though I am sending the user ID in the querystring when connecting to the websocket to do this. The user is not automatically available in the websocket scope.
I am unsure if there are any potential security risks as the documentation mentions that their recommendation is insecure, I do check that the user.is_authenticated. So I believe I have secured it.
I do believe that using the token created by djoser would be better though I am not sure how to send headers with the websocket request unless I include the token in the querystring instead of the user's ID.
I am keen to hear what the best practices are.
I am passing the user ID to the websocket via querystring as follows at the frontend:
websocket.value = new WebSocket(`ws://127.0.0.1:8000/ws/marketwatch/? ${authStore.userId}`)
middleware.py
from channels.db import database_sync_to_async
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
#database_sync_to_async
def get_user(user_id):
User = get_user_model()
try:
user = User.objects.get(id=user_id)
except ObjectDoesNotExist:
return AnonymousUser()
else:
if user.is_authenticated:
return user
else:
return AnonymousUser()
class QueryAuthMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
scope['user'] = await get_user(int(scope["query_string"].decode()))
return await self.app(scope, receive, send)
consumers.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from channels.security.websocket import AllowedHostsOriginValidator
from api.middleware import QueryAuthMiddleware
from .routing import ws_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
application = ProtocolTypeRouter({
'http':get_asgi_application(),
'websocket': AllowedHostsOriginValidator(
QueryAuthMiddleware(
URLRouter(ws_urlpatterns)
)
)
})
After doing some extensive research I decided not to pass the id or the token via the querystring as this poses a risk due to this data being stored in the server logs.
IMO the best option with the least amount of risk was passing the token as a message to the websocket after the connection was established and then verifying the token; closing the websocket if invalid.
This meant not requiring the middleware previously implemented. In this particular project no other messages would be received from the client so I don't need to do any checking on the key of the message received. This could be changed for chat apps and other apps that will receive further messages from the client.
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
import json
from rest_framework.authtoken.models import Token
class MarketWatchConsumer(AsyncWebsocketConsumer):
#database_sync_to_async
def verify_token(self, token_dict):
try:
token = Token.objects.get(key=token_dict['token'])
except Token.DoesNotExist:
return False
else:
if token.user.is_active:
return True
else:
return False
async def connect(self):
await self.channel_layer.group_add('group', self.channel_name)
await self.accept()
async def receive(self, text_data=None, bytes_data=None):
valid_token = await self.verify_token(json.loads(text_data))
if not valid_token:
await self.close()
async def disconnect(self, code):
await self.channel_layer.group_discard('group', self.channel_name)

Telebot not responding to requests with FLask server

I am trying to deploy my telegram bot to Heroku using Flask. ALthough I followed the tutorials and set up everything the same I am not getting any result and my bot is not responding.
import requests
import pandas as pd
import telebot
from telebot import types
import random
import os
from flask import Flask, request
from requests.models import MissingSchema
URL_HEROKU = f"some_url"
TOKEN = os.environ['TOKEN']
bot = telebot.TeleBot(TOKEN, parse_mode=None)
app = Flask(__name__)
#bot.message_handler(commands=['start'])
def start(message):
#Init keyboard markup
msg = ''' Hello, how are you?'''
bot.reply_to(message, msg)
#bot.message_handler(commands=['random'])
#bot.message_handler(regexp=r'random')
def send_some(message):
bot.send_message(message.chat.id, text='Hello')
#app.route('/' + TOKEN, methods=['GET'])
def getMessage():
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
return "!", 200
#app.route("/")
def webhook():
bot.remove_webhook()
bot.set_webhook(url= URL_HEROKU + TOKEN)
return "!", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))
My Procfile is:
web: python telebreed.py
and my requirements.txt:
Flask==2.0.2
gunicorn==20.1.0
pandas==1.2.2
pyTelegramBotAPI==4.6.0
requests==2.26.0
do you see any mistake ? When I open my app in Heroku I only see "!" which is the character defined in getMessage() method.

flask_socketio client does not receive a data that is processed in another request used multiprocess

I am using flask with react in the client I am trying to make a loop to update data constantly but when I do the emit the data is not sent but the loop is done even if I put a string to send if it is reflected in the console.log
from flask import Flask, send_from_directory
from flask_socketio import SocketIO, emit, send
from flask_cors import CORS
from multiprocessing import Process, Queue
from servidor.api.api import Api_iv
import sys
ruta_template = '../../cliente/build'
app = Flask(__name__)#, static_folder=ruta_template+"/static/")
app.config['SECRET_KEY'] = '085i5RIlQM'
socket = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet')
CORS(app)
#sys.setrecursionlimit(5000)
class Server_api:
def __init__(self, socket):
self.socket = socket
def Get_api(self, q):
api = Api_iv()
data = api.Get_data()
data_api = api.Parser_data(data)
q.put(data_api)
return
def Multiprocess_api(self):
data_process = Queue()
process = Process(target=self.Get_api(data_process), args=(data_process,))
process.start()
process.join()
return data_process.get()
#socket.on('stream')
def Stream_data():
api = Server_api(socket)
while True:
print("Obteniendo Partidos...")
emit("partidos", api.Multiprocess_api())
socket.sleep(5)
if __name__ == '__main__':
socket.run(app, debug=True)
client
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import io from 'socket.io-client';
const socket = io("http://127.0.0.1:5000");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
socket.emit("stream");
socket.on("partidos", (data) =>{
console.log(data);
})
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
reportWebVitals();
in the console.log nothing is reflected but on the server side if you do a loop and it is not seen that it is stuck only that it does not send the value
I already saw the problem, it was that I am sending data with many characters apparently sockeio cannot send data with many characters so I had to partition the sending into several parts so that it could be sent successfully ...

Adding Blueprints to Flask seems to break logging

In my Flask server app, I wanted to split up my routes into separate files so I used Blueprint. However this caused logging to fail within the constructor function used by a route. Can anyone see what I might have done wrong to cause this?
Simplified example ...
main.py ...
#!/usr/bin/python
import logging
import logging.handlers
from flask import Flask, Blueprint
from my_routes import *
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler("flask.log",
maxBytes=3000000, backupCount=2)
formatter = logging.Formatter(
'[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logging.getLogger().addHandler(logging.StreamHandler())
logging.debug("started app")
app = Flask(__name__)
app.register_blueprint(api_v1_0)
if __name__ == '__main__':
logging.info("Starting server")
app.run(host="0.0.0.0", port=9000, debug=True)
my_routes.py ...
import logging
import logging.handlers
from flask import Flask, Blueprint
class Class1():
def __init__(self):
logging.debug("Class1.__init__()") # This statement does not get logged
self.prop1=11
def method1(self):
logging.debug("Class1.method1()")
return self.prop1
obj1 = Class1()
api_v1_0 = Blueprint('api_v1_0', __name__)
#api_v1_0.route("/route1", methods=["GET"])
def route1():
logging.debug("route1()")
return(str(obj1.method1()))
You create an instance of Class1 in the global scope of module my_routes.py, so the constructor runs at the time you import that module, the from my_routes import * line in main.py. This is before your logging handler is configured, so there is nowhere to log at that time.
The solution is simple, move your import statement below the chunk of code that sets up the logging handler.