mirror of
https://github.com/RoboSats/robosats.git
synced 2024-09-11 22:07:08 +03:00
Add Chat endpoint to API v0 (#288)
* Add /api/chat route and GET method * Add message POST method * Wrap /api/chat GET in /api/order GET * Add send channel message on POST request * Fix OAS schema bug
This commit is contained in:
parent
f3d36fb95e
commit
79dad7afe2
@ -448,6 +448,8 @@ class OrderPublicSerializer(serializers.ModelSerializer):
|
||||
"maker_status",
|
||||
"price",
|
||||
"escrow_duration",
|
||||
"satoshis_now",
|
||||
"bond_size"
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.urls import path
|
||||
from .views import MakerView, OrderView, UserView, BookView, InfoView, RewardView, PriceView, LimitView, HistoricalView, TickView, StealthView
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
||||
from chat.views import ChatView
|
||||
|
||||
urlpatterns = [
|
||||
path('schema/', SpectacularAPIView.as_view(), name='schema'),
|
||||
@ -20,4 +21,5 @@ urlpatterns = [
|
||||
path("historical/", HistoricalView.as_view()),
|
||||
path("ticks/", TickView.as_view()),
|
||||
path("stealth/", StealthView.as_view()),
|
||||
path("chat/", ChatView.as_view({"get": "get","post":"post"})),
|
||||
]
|
||||
|
@ -8,14 +8,12 @@ from rest_framework.exceptions import bad_request
|
||||
from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
import textwrap
|
||||
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.contrib.auth.models import User
|
||||
from api.oas_schemas import BookViewSchema, HistoricalViewSchema, InfoViewSchema, LimitViewSchema, MakerViewSchema, OrderViewSchema, PriceViewSchema, RewardViewSchema, StealthViewSchema, TickViewSchema, UserViewSchema
|
||||
|
||||
from chat.views import ChatView
|
||||
from api.serializers import InfoSerializer, ListOrderSerializer, MakeOrderSerializer, OrderPublicSerializer, UpdateOrderSerializer, ClaimRewardSerializer, PriceSerializer, UserGenSerializer, TickSerializer, StealthSerializer
|
||||
from api.models import LNPayment, MarketTick, OnchainPayment, Order, Currency, Profile
|
||||
from control.models import AccountingDay, BalanceLog
|
||||
@ -28,7 +26,6 @@ from .nick_generator.nick_generator import NickGenerator
|
||||
from robohash import Robohash
|
||||
from scipy.stats import entropy
|
||||
from math import log2
|
||||
import numpy as np
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from datetime import timedelta, datetime
|
||||
@ -370,6 +367,10 @@ class OrderView(viewsets.ViewSet):
|
||||
data["asked_for_cancel"] = True
|
||||
else:
|
||||
data["asked_for_cancel"] = False
|
||||
|
||||
offset = request.GET.get('offset', None)
|
||||
if offset:
|
||||
data["chat"] = ChatView.get(None, request).data
|
||||
|
||||
# 9) If status is 'DIS' and all HTLCS are in LOCKED
|
||||
elif order.status == Order.Status.DIS:
|
||||
|
@ -2,6 +2,7 @@ from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
from channels.db import database_sync_to_async
|
||||
from api.models import Order
|
||||
from chat.models import ChatRoom, Message
|
||||
from asgiref.sync import async_to_sync
|
||||
|
||||
import json
|
||||
|
||||
|
26
chat/serializers.py
Normal file
26
chat/serializers.py
Normal file
@ -0,0 +1,26 @@
|
||||
from rest_framework import serializers
|
||||
from chat.models import Message
|
||||
|
||||
class ChatSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = (
|
||||
"index",
|
||||
"sender",
|
||||
"PGP_message",
|
||||
"created_at",
|
||||
)
|
||||
depth = 0
|
||||
|
||||
class PostMessageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ("PGP_message","order","offset")
|
||||
depth = 0
|
||||
|
||||
offset = serializers.IntegerField(allow_null=True,
|
||||
default=None,
|
||||
required=False,
|
||||
min_value=0,
|
||||
help_text="Offset for message index to get as response")
|
206
chat/views.py
206
chat/views.py
@ -1,6 +1,202 @@
|
||||
from django.shortcuts import render
|
||||
from operator import index
|
||||
from rest_framework import status, viewsets
|
||||
from chat.serializers import ChatSerializer, PostMessageSerializer
|
||||
from chat.models import Message, ChatRoom
|
||||
from api.models import Order, User
|
||||
from rest_framework.response import Response
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
# def room(request, order_id):
|
||||
# return render(request, 'chatroom.html', {
|
||||
# 'order_id': order_id
|
||||
# })
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
class ChatView(viewsets.ViewSet):
|
||||
serializer_class = PostMessageSerializer
|
||||
lookup_url_kwarg = ["order_id","offset"]
|
||||
|
||||
queryset = Message.objects.filter(order__status__in=[Order.Status.CHA, Order.Status.FSE])
|
||||
|
||||
def get(self, request, format=None):
|
||||
"""
|
||||
Returns chat messages for an order with an index higher than `offset`.
|
||||
"""
|
||||
|
||||
order_id = request.GET.get("order_id", None)
|
||||
offset = request.GET.get("offset", 0)
|
||||
|
||||
if order_id is None:
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"Order ID does not exist"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
order = Order.objects.get(id=order_id)
|
||||
|
||||
if not (request.user == order.maker or request.user == order.taker):
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"You are not participant in this order"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if not order.status in [Order.Status.CHA, Order.Status.FSE]:
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"Order is not in chat status"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
queryset = Message.objects.filter(order=order, index__gt=offset)
|
||||
chatroom = ChatRoom.objects.get(order=order)
|
||||
|
||||
# Poor idea: is_peer_connected() mockup. Update connection status based on last time a GET request was sent
|
||||
if chatroom.maker == request.user:
|
||||
chatroom.taker_connected = order.taker_last_seen > (timezone.now() - timedelta(minutes=1))
|
||||
chatroom.maker_connected = True
|
||||
chatroom.save()
|
||||
peer_connected = chatroom.taker_connected
|
||||
elif chatroom.taker == request.user:
|
||||
chatroom.maker_connected = order.maker_last_seen > (timezone.now() - timedelta(minutes=1))
|
||||
chatroom.taker_connected = True
|
||||
chatroom.save()
|
||||
peer_connected = chatroom.maker_connected
|
||||
|
||||
|
||||
messages = []
|
||||
for message in queryset:
|
||||
d = ChatSerializer(message).data
|
||||
print(d)
|
||||
# Re-serialize so the response is identical to the consumer message
|
||||
data = {
|
||||
'index':d['index'],
|
||||
'time':d['created_at'],
|
||||
'message':d['PGP_message'],
|
||||
'nick': User.objects.get(id=d['sender']).username
|
||||
}
|
||||
messages.append(data)
|
||||
|
||||
response = {'peer_connected': peer_connected, 'messages':messages}
|
||||
|
||||
return Response(response, status.HTTP_200_OK)
|
||||
|
||||
def post(self, request, format=None):
|
||||
"""
|
||||
Adds one new message to the chatroom.
|
||||
"""
|
||||
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
# Return bad request if serializer is not valid
|
||||
if not serializer.is_valid():
|
||||
context = {"bad_request": "Invalid serializer"}
|
||||
return Response(context, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
print(request)
|
||||
order_id = serializer.data.get("order", None)
|
||||
|
||||
if order_id is None:
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"Order ID does not exist"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
order = Order.objects.get(id=order_id)
|
||||
|
||||
if not (request.user == order.maker or request.user == order.taker):
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"You are not participant in this order"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if not order.status in [Order.Status.CHA, Order.Status.FSE]:
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"Order is not in chat status"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if order.maker == request.user:
|
||||
sender = order.maker
|
||||
receiver = order.taker
|
||||
elif order.taker == request.user:
|
||||
sender = order.taker
|
||||
receiver = order.maker
|
||||
|
||||
chatroom, _ = ChatRoom.objects.get_or_create(
|
||||
id=order_id,
|
||||
order=order,
|
||||
room_group_name=f"chat_order_{order_id}",
|
||||
defaults={
|
||||
"maker": order.maker,
|
||||
"maker_connected": order.maker == request.user,
|
||||
"taker": order.taker,
|
||||
"taker_connected": order.taker == request.user,
|
||||
}
|
||||
)
|
||||
|
||||
last_index = Message.objects.filter(order=order, chatroom=chatroom).count()
|
||||
new_message = Message.objects.create(
|
||||
index=last_index+1,
|
||||
PGP_message=serializer.data.get("PGP_message"),
|
||||
order=order,
|
||||
chatroom=chatroom,
|
||||
sender=sender,
|
||||
receiver=receiver,
|
||||
)
|
||||
|
||||
# Send websocket message
|
||||
if chatroom.maker == request.user:
|
||||
peer_connected = chatroom.taker_connected
|
||||
elif chatroom.taker == request.user:
|
||||
peer_connected = chatroom.maker_connected
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
f"chat_order_{order_id}",
|
||||
{
|
||||
"type": "PGP_message",
|
||||
"index": new_message.index,
|
||||
"message": new_message.PGP_message,
|
||||
"time": str(new_message.created_at),
|
||||
"nick": new_message.sender.username,
|
||||
"peer_connected": peer_connected,
|
||||
}
|
||||
)
|
||||
|
||||
# if offset is given, reply with messages
|
||||
offset = serializer.data.get("offset", None)
|
||||
if offset:
|
||||
queryset = Message.objects.filter(order=order, index__gt=offset)
|
||||
messages = []
|
||||
for message in queryset:
|
||||
d = ChatSerializer(message).data
|
||||
print(d)
|
||||
# Re-serialize so the response is identical to the consumer message
|
||||
data = {
|
||||
'index':d['index'],
|
||||
'time':d['created_at'],
|
||||
'message':d['PGP_message'],
|
||||
'nick': User.objects.get(id=d['sender']).username
|
||||
}
|
||||
messages.append(data)
|
||||
|
||||
response = {'peer_connected': peer_connected, 'messages':messages}
|
||||
else:
|
||||
response = {}
|
||||
|
||||
return Response(response, status.HTTP_200_OK)
|
||||
|
Loading…
Reference in New Issue
Block a user