Difference in the receive and send method in a websocket - python-3.x

I am looking over the following code which does a 'group chat' with different members:
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': 'OK'
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
My questions is what do the two items do? I see that receive(), also does the group_send, so what purpose does the chat_message have if the receive sends it upon receiving it?

That chat server code is a simple example on how to send group messages.
In the code:
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': 'OK'
}
)
this line
'type': 'chat_message',
is responsible for calling method chat_message() with { 'message': 'OK'}
Before sending this message to the group members you may want to modify or check the data, or need to do other stuff. That's why self.channel_layer.group_send doesn't directly sends message to the group but calls another method (in this case chat_message) to handle sending of message and to keep receive() method's code clean.

Related

What's the right way to execute a Twilio Studio conversation flow?

Im learning how to set a twilio studio flow with python, I'm currently testing one of the templates that Twilio provides, and Im communicating with the bot from WhatsApp. However, I can only send the first message of the flow and if I send another message, this message pop up:
Unable to create record: Execution XXXXXXXXXXXXXXXXXXXXX is already active for this contact. End the active Execution before creating a new one
I tried to add .update(status='ended') to my variable, but it just kinda looped every time I sent a message, I know that every time that theres an incoming message it will trigger the conversation. So my question is, how can I continue the conversation flow without creating a new trigger every time that theres an incomming message?
Here's my flow in case it's necessary.
And this is the functions and endpoints that I'm using to trigger the action:
# twilio.route('/incoming_message', methods=['GET', 'POST'])
def incoming_message_data() -> str:
if request.method == 'POST':
response = {}
error, message, code = False, '', ''
message = incoming_message()
response.update({'sucess': True, 'message': message, 'message': f'{message}', 'status_code': 200, 'error': None, 'code': f'{code}'} if message and message != [{}]else {
'sucess': False, 'message': 'Message could not be sent', 'status_code': 400, 'error': f'{error}', 'code': f'{code}'})
return message
def twilio_studio_flow(phone_number: str) -> str:
'''
Twilio Studio Flow
'''
response = request.values.get('Body', '').lower()
execution = twilio_client.studio \
.v2 \
.flows(Config.TWILIO_STUDIO_FLOW_SID) \
.executions \
.create(to=(f'whatsapp:{phone_number}'), from_=Config.TWILIO_PHONE_NUMBER,
parameters={
"appointment_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
})\
.update(status='ended')
def validate_phone_number(phone_number: str) -> bool:
'''
Validate phone number
'''
try:
phone = phonenumbers.parse(phone_number.strip(), None)
client = Client.query.filter_by(phone=phone_number).first()
return phonenumbers.is_valid_number(phone) and client is not None
except Exception:
return False
def incoming_message() -> str:
'''
Receive incoming messages
'''
# Get the message the user sent our Twilio number
incoming_message = request.values.get('Body', '').lower()
# Get the phone number of the person sending the text message
phone_number = request.values.get('From', None).replace('whatsapp:', '')
resp = MessagingResponse()
if validate_phone_number(phone_number) and incoming_message:
resp.message(twilio_studio_flow(phone_number))
else:
resp.message(
'Lo sentimos, no pudimos validar tu numero de telefono 😟')
return str(resp)
Thanks in advance for helping me :).
As I mentioned previously, I want to know the right way to execute a Twilio Studio. Honestly, I cheked the docs but it's a little bit unclear on how to this.
Based on your answer in the comments, I'd suggest to link the Studio flow directly to the WhatsApp sender in the console (instead of invoking the flow manually via the API).
To connect the Studio Flow to your WhatsApp number ("Sender"). Click on the Trigger (Start) Widget and locate the Webhook URL field in the right-hand menu. Copy that URL to your clipboard.
Next, navigate to your WhatsApp Senders in the Twilio console. Click to select the sender that you want to use with this Studio Flow. Paste the Webhook URL that you copied from your Studio Flow into the field Webhook URL for incoming messages. Don't forget to click Update WhatsApp Sender.
Now, any time you receive an inbound message on your selected WhatsApp-enabled sender (number), it will be routed to your new Studio Flow.
Taken from the documentation.

Gmail API - How to relabel emails as spam using Filters?

I am attempting to write a bot which searches for an email by a keyword, then creates a filter sending all future emails from that sender to spam.
Here is my code:
async def remove_subscription(service, from_email, message):
# labels = service.users().labels().list(userId='me').execute().get('labels', [])
# for label in labels:
# print(label['id'])
filter_content = {
'criteria': {
'from': from_email
},
'action': {
'addLabelIds': ['SPAM'],
'removeLabelIds': ['INBOX']
}
}
try:
result = service.users().settings().filters().create(
userId='me', body=filter_content).execute()
print(F'Created filter with id: {result.get("id")}')
return True
except:
print('An error occurred')
await message.channel.send('An error occurred.')
return False
As you can see, it is pretty much the given code from the google docs:
https://developers.google.com/gmail/api/guides/filter_settings?hl=en
The api won't allow me to use addLabelIds: ['SPAM'], and it does not work while attempting to do the same query on the gmail docs in-webpage testing (So I know it isnt an issue with the rest of my code). Here is the error:
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://gmail.googleapis.com/gmail/v1/users/me/settings/filters?alt=json returned "Invalid label SPAM in AddLabelIds". Details: "[{'message': 'Invalid label SPAM in AddLabelIds', 'domain': 'global', 'reason': 'invalidArgument'}]">
I can't seem to find an explanation anywhere which states that I can't add SPAM as a label ID. I confirmed with lines 2-4 that the label ID is indeed 'SPAM".
Is this just not allowed by the api? How could I create a filter which sends all emails from a specific sender to spam?
Thanks ahead of time.
Issue:
I don't think Gmail allows messages to be sent to SPAM via filters. This is true not just for the API, but for the user-interface too:
If you'd like this feature to be added to Gmail, I'd suggest requesting it via Send feedback on the user-interface. Nevertheless, it's likely that this is a deliberate limitation. In any case, you could use a custom label to act as SPAM.
Related:
How to send messages to spam in Gmail filter?

Can I send slack alert message through bot using python?

I want to alert everyone on Slack App, which I can do manually by sending #here in the channel.
But I want to send it using my bot.
So, I tried this:
import numpy as np
import requests
data = {
'token': token,
'channel': channel,
'as_user': True,
'text': "<#here>"
}
requests.post(url='https://slack.com/api/chat.postMessage', data=data)
But it send just a usual message, not an alert message.
How can I send an alert message using my bot?

sending response to particular django websocket client from rest api or a server

consumer.py
# accept websocket connection
def connect(self):
self.accept()
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
command = text_data_json['command']
job_id = text_data_json['job_id']
if command == 'subscribe':
self.subscribe(job_id)
elif command == 'unsubscribe':
self.unsubscribe(job_id)
else:
self.send({
'error': 'unknown command'
})
# Subscribe the client to a particular 'job_id'
def subscribe(self, job_id):
self.channel_layer.group_add(
'job_{0}'.format(job_id),
self.channel_name
)
# call this method from rest api to get the status of a job
def send_job_notification(self, message, job_id):
channel_layer = get_channel_layer()
group_name = 'job_{0}'.format(job_id)
channel_layer.group_send(
group_name,
{
"type": "send.notification",
"message": message,
}
)
# Receive message from room group
def send_notification(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps(
message))
In the above code what I am trying to do is connect clients to the socket and subscribe clients to a particular "job_id" by creating a group called "job_1" using "subscribe" method and add it to the channel layer. Creation of groups are dynamic.
I am using below "simple websocket client extension" from Google to connect to the above websocket. I am able to make a connection with the websocket and send request to it as shown in the picture below.
Now since the client is connected and subscribed to a particular "job_id",
I am using "Postman" to send notification to the above connected client(simple websocket client extension) subscribed to particular "job_id" by passing in the job_id in the request as highlighted in yellow below.
when I do a post to the "REST-API" I am calling "send_job_notification(self, message, job_id)" method of "consumer.py" file along with the "job_id" as '1' shown in the picture below in yellow
After doing all this I don't see any message sent to the connected client subscribed to a "job_id" from the "REST-API" call.
Any help would be highly appreciated as it has been dragging on for a long time.
Edit:
thank you for the suggestion Ken its worth to make the method as "#staticmethod" but Ken how do I make the API send job status updates to the connected Clients because my long running jobs would run in some process and send update messages back to the backend via REST-API and the updates then need to be sent to the correct Client (via websockets).
My API call to the socket consumer is as below:
from websocket_consumer import consumers
class websocket_connect(APIView):
def post(self, request, id):
consumers.ChatConsumer.send_job_notification("hello",id)
My socket consumer code is as below:
Edit
`CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}`
As you can see 'Redis' service is also running
Edit-1
You cannot call the method in the consumer directly from an external code because you need to get the particular consumer instance connected to your client. This is the job of the channel layer achieved by using a message passing system or broker as reddis.
From what I can see, you're already going towards the right direction, except that the send_job_notification is an instance method which will require instantiating the consumer. Make it a static method instead, so you can call it directly without a consumer instance
#staticmethod
def send_job_notification(message, job_id):
channel_layer = get_channel_layer()
group_name = 'job_{0}'.format(job_id)
channel_layer.group_send(
group_name,
{
"type": "send.notification",
"message": message,
}
And in your API view, you can simply call it as:
ChatConsumer.send_job_notification(message, job_id)

aiohttp api error handler framework

we have developed some of the aiohttp server side apis and from that api we am calling one of the python class, where i have done all business logic.
now we want to create a error handling framework for all apis, please give some ideas to implement that framework and i need to do request parameters validations as well, should i consolidate and send back all error at one time or just check one parameter send back the error message to caller.
api look like this:
async def new_user(request):
try:
# happy path where name is set
user = request.query['name']
# Process our new user
print("Creating new user with name: " , user)
response_obj = { 'status' : 'success' }
# return a success json response with status code 200 i.e. 'OK'
return web.Response(text=json.dumps(response_obj), status=200)
except Exception as e:
# Bad path where name is not set
response_obj = { 'status' : 'failed', 'reason': str(e), 'code' : 400 }
# return failed with a status code of 500 i.e. 'Server Error'
return web.Response(text=json.dumps(response_obj), status=400)
If you are using aio-http try to create aiohttp.web.middleware.
https://docs.aiohttp.org/en/stable/web_advanced.html#middlewares

Resources