Python-socketio: How to emit message regulary from server to client? - python-socketio

I want to send a message from server to client at 1 second intervals,
writing the string 'send clock' to console before the message is sent.
However, client does not receive "clock" message.
I have got the echo message is working fine, so it's not a connection issue.
How can I send a message at 1 second intervals?
Server code(python):
import threading
import time
import socketio
import eventlet
socket = socketio.Server(async_mode='eventlet')
app = socketio.WSGIApp(socket)
#socket.on('echo')
def echo(sid, message):
socket.emit('echo', message)
def worker1():
eventlet.wsgi.server(eventlet.listen(('', 5030)), app)
def worker2():
while(1):
print("send clock")
socket.emit('clock', '1 sec')
time.sleep(1)
def main():
t1 = threading.Thread(target=worker1)
t2 = threading.Thread(target=worker2)
t1.start()
t2.start()
if __name__ == '__main__':
main()
Client code(nodejs):
const io = require("socket.io-client");
socket = io("http://xxx.xxx.xxx.xxx:5030", {
transports: ["websocket"],
});
socket.emit("echo", "test");
socket.on("echo", (data) => {
console.log(data);
});
socket.on("clock", (data) => {
console.log("receive clock");
});
My server side environment:
python: 3.7.3
python-socketio: 4.5.1
eventlet: 0.25.1
Thank you for reading it until the very end. <3

You can't combine threads or the sleep() function with eventlet, you should use greenlet based equivalents instead. The easiest way to do this is to start your background tasks with the socket.start_background_task() helper function, and sleep with socket.sleep(). I'm writing this by memory so there might be small mistakes, but your application could do something like the following:
import socketio
import eventlet
socket = socketio.Server(async_mode='eventlet')
app = socketio.WSGIApp(socket)
#socket.on('echo')
def echo(sid, message):
socket.emit('echo', message)
def worker1():
eventlet.wsgi.server(eventlet.listen(('', 5030)), app)
def worker2():
while(1):
print("send clock")
socket.emit('clock', '1 sec')
socket.sleep(1)
def main():
socket.start_background_task(worker2)
worker1()
if __name__ == '__main__':
main()

Related

12200 - Schema validation warning twilio

Hi team i need to fix the 12200 - Schema validation warning twilio.
everything is working but i dont receive the whatsapp respond back .
enter image description here
here is the app.py code:
from helper.openai_api import text_complition
from helper.twilio_api import send_message
from twilio.twiml.messaging_response import MessagingResponse
from flask import Flask, request
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
#app.route('/')
def home():
return 'All is well...'
#app.route('/twilio/receiveMessage', methods=['POST'])
def receiveMessage():
try:
message = request.form['Body']
sender_id = request.form['From']
# Placeholder code
result = {}
result['status'] = 1
result['response'] = "Hi, I'm CODAI, I have received your message."
send_message(sender_id, result['response'])
except:
pass
return 'OK', 200
here is the openai.py code:
import os
import openai
from dotenv import load_dotenv
from twilio.twiml.messaging_response import MessagingResponse
load_dotenv()
openai.api_key = os.getenv('OPENAI_API_KEY')
def text_complition(prompt: str) -> dict:
'''
Call Openai API for text completion
Parameters:
- prompt: user query (str)
Returns:
- dict
'''
try:
response = openai.Completion.create(
model='text-davinci-003',
prompt=f'Human: {prompt}\nAI: ',
temperature=0.9,
max_tokens=150,
top_p=1,
frequency_penalty=0,
presence_penalty=0.6,
stop=['Human:', 'AI:']
)
return {
'status': 1,
'response': response['choices'][0]['text']
}
except:
return {
'status': 0,
'response': ''
}
here is the twilio.py code:
import os
from twilio.twiml.messaging_response import MessagingResponse
from twilio.rest import Client
from dotenv import load_dotenv
load_dotenv()
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
client = Client(account_sid, auth_token)
def send_message(to: str, message: str) -> None:
'''
Send message through Twilio's WhatsApp API.
Parameters:
- to(str): recipient's phone number in the format of "whatsapp:+[country code][phone number]"
- message(str): text message to send
Returns:
- None
'''
_ = client.messages.create(
from_=os.getenv('FROM'),
body=message,
to="whatsapp:" + to
)
i need help to fix the error 12200 - Schema validation warning twilio

Why Twitter is not making a get request to my server while registering webhook using endpoint?

I'm using twitter account activity api for registering a webhook using autohook wrapper library.
As per the docs the post requests to endpoint (https://api.twitter.com/1.1/account_activity/all/:env_name/webhooks.json) should trigger a get request from my server. that will return a crc based on a crc token and my consumer key.
While im getting code 215 and unable to connect during crc request error message again and again.
Im not sure why get request is not exceuting. here is my server and client code.
server.py
#app.route("/webhook/twitter", methods=["GET", "POST"])
def callback() -> json:
if flask.request.method == "GET" or flask.request.method == "PUT":
print(flask.request.args.get("crc_token"))
hash_digest = hmac.digest(
key=os.environ["consumer_secret"].encode("utf-8"),
msg=flask.request.args.get("crc_token").encode("utf-8"),
digest=hashlib.sha256,
)
return {
"response_token": "sha256="
+ base64.b64encode(hash_digest).decode("ascii")
}
elif flask.request.method == "POST":
data = flask.request.get_json()
logging.info(data)
return {"code": 200}
# Once the code running on the server.
# You can register and subscribe to events from your local machine.
#app.route("/",methods=["GET"])
def callbackget():
return "in GEt"
if __name__ == "__main__":
app.run(debug=True, port=443,ssl_context=context)
client side autohook wrapper:
const { Autohook } = require('twitter-autohook');
(async ƛ => {
const webhook = new Autohook(
{
token:"1218220064610099203-8CnN6G3edmIRGdDpLcR7Bao88mMCUg",
token_secret:"JEavuf3PZmSsXWhmSdtNarFPNBp6BsZzMrLpkEMASkJX5",
consumer_key:"6n7U6KDXxouXDcsLMxdiSKwZ9",
consumer_secret:"o2aKE9TivbTwchJXXZ4XaTyAwtCgtoRO35a9PgdaqMLUBRfv49",
env:"enc"
}
);
// Removes existing webhooks
await webhook.removeWebhooks();
// Listens to incoming activity
webhook.on('event', event => console.log('Something happened:', event));
// Starts a server and adds a new webhook
await webhook.start("https://127.0.0.1:443/webhook/twitter");
// Subscribes to a user's activity
await webhook.subscribe({oauth_token, oauth_token_secret});
})();

MQTT Broker does not deliver the messages sent by publisher, on time

I wrote this MQTT publisher code:
import paho.mqtt.client as mqtt
import time
HOST = "localhost"
PORT = 1883
KEEP_ALIVE_INT = 100
TOPIC = "noti"
def sendMsg():
MSG = ["1111", "2222", "3333", "4444", "5555"]
i = 0
try:
while i<5:
client.publish(TOPIC, MSG[i], qos=0)
i+=1
time.sleep(1)
except Exception as e:
print("Caught Exception: " + e)
def onConnect(client, userdata, flags, rc):
if rc == 0:
print("Connected successfully")
sendMsg()
else:
print("Connection failed, result code: " + str(rc))
def onPublish(client, userdata, mid):
print ("Message is published")
client = mqtt.Client("pub")
client.on_connect = onConnect
client.on_publish = onPublish
client.connect(HOST, PORT, KEEP_ALIVE_INT)
client.loop_forever()
And, the following is the MQTT subscriber code:
import paho.mqtt.client as mqtt
import time
HOST = "localhost"
PORT = 1883
KEEP_ALIVE_INT = 100
TOPIC = "noti"
def onConnect(client, userdata, flags, rc):
if rc == 0:
print("=> Connected successfully")
client.subscribe(TOPIC, 0)
else:
print("=> Connection failed, result code: " + str(rc))
def onSubscribe(mosq, obj, mid, granted_qos):
print ("=> Subscribed to topic: " + TOPIC)
print ("Granted QOS: "+str(granted_qos))
def onMessage(client, userdata, msg):
print("=> Received message: " + msg.topic +" - " + msg.payload.decode("utf-8"))
client = mqtt.Client("sub")
client.on_message = onMessage
client.on_connect = onConnect
client.on_subscribe = onSubscribe
client.connect(HOST, PORT, KEEP_ALIVE_INT )
client.loop_forever()
I am using Mosquitto broker in my PC.
The publish is done in every 1 second, but I can see the print "Message is published" 5 times after all 5 messages are published. Also, the subscriber receives the messages together after 5 seconds, not in every 1 second.
Please help me understand the mistake, suggest modification.
This is because all callbacks and message handling happen on the client network loop thread and you are blocking that thread by not returning from the on_connect() callback.
So the calls to client.publish() are queued up until the on_connect() callback returns.
You need to find a way to trigger the sendMsg() function not on the client loop. (Probably on a separate thread)

async endpoints for swagger generated server using tornado

I have generated a server using swagger editor. Then I move to using Tornado as http-server like:
def main():
app = App(system_manager=system_manager, import_name=__name__,
specification_dir='./swagger/', server='tornado')
app.app.json_encoder = encoder.JSONEncoder
app.add_api('swagger.yaml', arguments={
'title': 'API'}, pythonic_params=True)
app.run(port=8085)
Where App is:
class App(connexion.App):
def __init__(self, system_manager, import_name, server='tornado', **kwargs):
super(App, self).__init__(import_name, server=server, **kwargs)
if not issubclass(type(system_manager), SystemManager):
raise ValueError(
"App.init: 'system_manager' is not a subclass of 'SystemManager'")
self.__system_manager = system_manager
def run(self, port=None, server=None, debug=None, host=None, **options):
server_type = server or self.server
if server_type != 'tornado':
super(App, self).run(port=port, server=server,
debug=debug, host=host, **options)
return None
if port is not None:
self.port = port
elif self.port is None:
self.port = 5000
self.host = host or self.host or '0.0.0.0'
if server is not None:
self.server = server
if debug is not None:
self.debug = debug
wsgi_container = tornado.wsgi.WSGIContainer(self.app)
http_server = tornado.web.Application([
(r'/websocket/.*', WebSocket, dict(system_manager=self.__system_manager)),
(r'^/v1/wifi(/all)*$', AsyncFallbackHandler,
dict(fallback=wsgi_container)),
(r'.*', tornado.web.FallbackHandler, dict(fallback=wsgi_container))
], websocket_ping_interval=5)
http_server.listen(self.port, address=self.host)
tornado.ioloop.IOLoop.instance().start()
For some reasons I have some endpoints that are taking ~30secs to respond and because I am using WSGIContainer all the requests are synchronous. Which means that every request comming after those ones will be dealyed until they are done. Cite from documentation:
WSGI is a synchronous interface, while Tornado's concurrency model is
based on single-threaded asynchronous execution. This means that
running a WSGI app with Tornado's WSGIContainer is less scalable than
running the same app in a multi-threaded WSGI server like gunicorn or
uwsgi.
I have tried to:
Keep using WSGIContainer but in a handler that will make calls async. Didn't work out. I'm getting: RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0'
class AsyncFallbackHandler(tornado.web.RequestHandler):
def initialize(
self, fallback: Callable[[httputil.HTTPServerRequest], None]
) -> None:
self.fallback = fallback
async def prepare(self, *args, **kwargs):
await self.run_in_executor()
self._finished = True
self.on_finish()
async def run_in_executor(self):
loop = tornado.ioloop.IOLoop.instance().asyncio_loop
done, pending = await asyncio.wait(
fs=[loop.run_in_executor(None, self.fallback, self.request)],
return_when=asyncio.ALL_COMPLETED
)
Create another RequestHandler which is not using WSGIContainer. But here when the request is 404 it fails to json encode ConnexionResponse. Neither I can write a ConnexionResponse to pipe as it has to be string/bytes/dict.
class WifiRequestHandler(tornado.web.RequestHandler):
async def get(self, *args, **kwargs):
# await tornado.gen.sleep(20)
ids = self.get_arguments('ids')
method = get_wifi
if self.request.path.startswith('/v1/wifi/all'):
method = get_wifi_all
self.write(await self.run_in_executor(method, ids))
self.set_header('Content-Type', 'application/json')
async def post(self, *args, **kwargs):
logger.info(kwargs)
body = tornado.escape.json_decode(self.request.body)
self.write(await self.run_in_executor(update_wifi, body))
self.set_header('Content-Type', 'application/json')
async def run_in_executor(self, method, *args):
loop = tornado.ioloop.IOLoop.instance().asyncio_loop
done, pending = await asyncio.wait(
fs=[loop.run_in_executor(None, method, args)],
return_when=asyncio.ALL_COMPLETED
)
result = done.pop().result()
if type(result) is ConnexionResponse:
return result
enc = JSONEncoder()
return enc.encode(result)
Please help me find a way to make some of my endpoints asynchronous
I made my second solution to work by unpacking the ConnexionResponse and setting the status_code. This is how it looks after fix:
async def run_in_executor(self, method, *args):
loop = tornado.ioloop.IOLoop.instance().asyncio_loop
done, pending = await asyncio.wait(
fs=[loop.run_in_executor(None, method, args)],
return_when=asyncio.ALL_COMPLETED
)
result = done.pop().result()
if type(result) is ConnexionResponse:
self.set_status(result.status_code)
return result.body
enc = JSONEncoder()
return enc.encode(result)

Messages in Django Channels sent outside consumer not being received

I'm trying to send messages to specific websocket instances, but neither channel_layer.send, nor using channel_layer.group_send with unique groups for each instance seems to be working, no errors are being raised, they just aren't received by the instances.
The function that sends the message is:
def listRequest(auth_user, city):
request_country = city["country_name"]
request_city = city["city"]
request_location = request_city +", "+request_country
concatenate_service_email = auth_user.service + "-" + auth_user.email
this_request = LoginRequest(service_and_email=concatenate_service_email, location=request_location)
this_request.generate_challenge()
this_request.set_expiry(timezone.now() + timezone.timedelta(minutes=5))
this_request.save()
channel_layer = get_channel_layer()
print(auth_user.current_socket)
async_to_sync(channel_layer.group_send)(
auth_user.current_socket,{
"type": "new.request",
"service_and_email" : concatenate_service_email
},
)
My current working consumers.py (receive and scanrequest don't have anything that's likely to be relevant to the issue):
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .helpers import verify_identity, unique_group
from django.utils import timezone
from .models import Authentication, LoginRequest
import json
import time
class AuthConsumer(WebsocketConsumer):
account_list =[]
def connect(self):
print("Connect attempted")
print(self.channel_name)
print(unique_group(self.channel_name))
async_to_sync(self.channel_layer.group_add)(unique_group(self.channel_name), self.channel_name)
self.accept()
def disconnect(self, close_code):
print("Disconnect attempted")
async_to_sync(self.channel_layer.group_discard)(unique_group(self.channel_name), self.channel_name)
for i in self.account_list:
serviceEmailSplit = i.split("-")
try:
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
auth_user.set_socket("NONE")
auth_user.save()
except:
print("Error user %s does not exist" %i)
pass
def receive(self, text_data):
print("Receiving data")
if text_data[0:7] == "APPROVE":
data_as_list = text_data.split(",")
serviceEmailSplit = data_as_list[1].split("-")
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
this_request = LoginRequest.objects.get(service_and_email=data_as_list[1],approved=False, expiry__gt=timezone.now())
if verify_identity(auth_user.public_key, data_as_list[2], this_request.challenge):
this_request.set_approved()
self.send("Request Approved!")
else:
self.send("ERROR: User verification failed")
else:
self.account_list = text_data.split(",")
self.account_list.pop(-1)
print(self.account_list)
for i in self.account_list:
serviceEmailSplit = i.split("-")
try:
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
auth_user.set_socket(unique_group(self.channel_name))
auth_user.save()
except:
self.send("Error user %s does not exist" %i)
self.scanRequest()
def scanRequest(self):
requestSet = LoginRequest.objects.filter(service_and_email__in = self.account_list, approved = False, request_expiry__gt = timezone.now())
if requestSet.count() > 0:
for request in requestSet:
self.send(request.service_and_email+","+request.location+","+str(request.challenge))
else:
self.send("NOREQUESTS")
def new_request(self,event):
print("NEW REQUEST!")
this_request = LoginRequest.objects.filter(service_and_email = event["service_and_email"]).latest('request_expiry')
self.send(this_request.service_and_email+","+this_request.location+","+str(this_request.challenge))
And my routing.py:
from django.urls import re_path
from . import consumers
from django.conf.urls import url
websocket_urlpatterns = [
url(r"^ws/$", consumers.AuthConsumer.as_asgi()),
]
"NEW REQUEST!" is never printed, having tried to call it both by sending a message directly, and neither does using groups like I have written above.
My redis server appears to be working from testing like the documentation for the channels tutorial suggests:
https://channels.readthedocs.io/en/stable/tutorial/part_2.html
I'm pretty stumped after attempts to fix it, and I've looked at the other posts on stackoverflow with the same/similar issues and I'm already following whatever solutions they have in my code.

Resources