Skip to content

Commit

Permalink
Merge pull request #28 from Reckless-Satoshi/asynchronous-chat
Browse files Browse the repository at this point in the history
Minimal chat. Just enough for it to work. Does not log any message, so admin won't be able to resolve disputes easily with this set up.
  • Loading branch information
Reckless-Satoshi authored Jan 13, 2022
2 parents fac9a62 + 18a8038 commit 89c5f7a
Show file tree
Hide file tree
Showing 24 changed files with 533 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .env-sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ LND_CERT_BASE64=''
LND_MACAROON_BASE64=''
LND_GRPC_HOST='127.0.0.1:10009'

REDIS_URL=''

# Market price public API
MARKET_PRICE_API = 'https://blockchain.info/ticker'

Expand Down
3 changes: 2 additions & 1 deletion api/logics.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ def gen_taker_hold_invoice(cls, order, user):
# If there was no taker_bond object yet, generates one
order.last_satoshis = cls.satoshis_now(order)
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel."
pos_text = 'Buying' if cls.is_buyer(order, user) else 'Selling'
description = f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {order.amount} - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel."

# Gen hold Invoice
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
Expand Down
8 changes: 5 additions & 3 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.utils.html import mark_safe
import uuid

from decouple import config
from pathlib import Path
Expand Down Expand Up @@ -40,6 +41,7 @@ class Status(models.IntegerChoices):


# payment use details
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HOLD)
concept = models.PositiveSmallIntegerField(choices=Concepts.choices, null=False, default=Concepts.MAKEBOND)
status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.INVGEN)
Expand All @@ -59,7 +61,7 @@ class Status(models.IntegerChoices):
receiver = models.ForeignKey(User, related_name='receiver', on_delete=models.CASCADE, null=True, default=None)

def __str__(self):
return (f'HTLC {self.id}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}')
return (f'LN-{str(self.id)[:8]}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}')

class Order(models.Model):

Expand Down Expand Up @@ -203,7 +205,7 @@ class MarketTick(models.Model):
maker and taker are commited with bonds (contract
is finished and cancellation has a cost)
'''

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
price = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)])
volume = models.DecimalField(max_digits=8, decimal_places=8, default=None, null=True, validators=[MinValueValidator(0)])
premium = models.DecimalField(max_digits=5, decimal_places=2, default=None, null=True, validators=[MinValueValidator(-100), MaxValueValidator(999)], blank=True)
Expand Down Expand Up @@ -235,6 +237,6 @@ def log_a_tick(order):
tick.save()

def __str__(self):
return f'Tick: {self.id}'
return f'Tick: {str(self.id)[:8]}'


1 change: 1 addition & 0 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def get(self, request, format=None):
data['is_maker'] = order.maker == request.user
data['is_taker'] = order.taker == request.user
data['is_participant'] = data['is_maker'] or data['is_taker']
data['ur_nick'] = request.user.username

# 3) If not a participant and order is not public, forbid.
if not data['is_participant'] and order.status != Order.Status.PUB:
Expand Down
Empty file added chat/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions chat/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions chat/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ChatConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'chat'
64 changes: 64 additions & 0 deletions chat/consumers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from channels.generic.websocket import AsyncWebsocketConsumer
from api.logics import Logics
from api.models import Order

import json

class ChatRoomConsumer(AsyncWebsocketConsumer):


async def connect(self):
self.order_id = self.scope['url_route']['kwargs']['order_id']
self.room_group_name = f'chat_order_{self.order_id}'
self.user = self.scope["user"]
self.user_nick = str(self.user)

# Forbit if user is not part of the order
# Does not work Async
# order = Order.objects.get(id=self.order_id)

# # Check if user is participant on the order.
# if not (Logics.is_buyer(order[0], self.user) or Logics.is_seller(order[0], self.user)):
# print ("Outta this chat")
# return False

print(self.user_nick)
print(self.order_id)

await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)

await self.accept()

async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)

async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
nick = text_data_json['nick']

await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chatroom_message',
'message': message,
'nick': nick,
}
)

async def chatroom_message(self, event):
message = event['message']
nick = event['nick']

await self.send(text_data=json.dumps({
'message': message,
'user_nick': nick,
}))

pass
3 changes: 3 additions & 0 deletions chat/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
6 changes: 6 additions & 0 deletions chat/routing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
re_path(r'ws/chat/(?P<order_id>\w+)/$', consumers.ChatRoomConsumer.as_asgi()),
]
82 changes: 82 additions & 0 deletions chat/templates/chatroom.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<html lang="en">

<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

<title>Hello, world!</title>
</head>

<body>

<div class="container">
<div class="row d-flex justify-content-center">
<div class="col-6">
<form>
<div class="form-group">
<label for="exampleFormControlTextarea1" class="h4 pt-5">Chatroom</label>
<textarea class="form-control" id="chat-text" rows="10"></textarea><br>
</div>
<div class="form-group">
<input class="form-control" id="input" type="text"></br>
</div>
<input class="btn btn-secondary btn-lg btn-block" id="submit" type="button" value="Send">
</form>
</div>
</div>
</div>

{{ request.user.username|json_script:"user_username" }}
{{ order_id|json_script:"order-id" }}

<script>
const user_username = JSON.parse(document.getElementById('user_username').textContent);
document.querySelector('#submit').onclick = function (e) {
const messageInputDom = document.querySelector('#input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message,
'username': user_username,
}));
messageInputDom.value = '';
};




const orderId = JSON.parse(document.getElementById('order-id').textContent);

const chatSocket = new WebSocket(
'ws://' +
window.location.host +
'/ws/chat/' +
orderId +
'/'
);

chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
document.querySelector('#chat-text').value += (data.username + ': ' + data.message + '\n')
}
</script>

<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous">
</script>
</body>

</html>
3 changes: 3 additions & 0 deletions chat/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
8 changes: 8 additions & 0 deletions chat/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path

from . import views

urlpatterns = [
path('', views.index, name='index'),
path('<str:order_id>/', views.room, name='order_chat'),
]
10 changes: 10 additions & 0 deletions chat/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.shortcuts import render


def index(request):
return render(request, 'index.html', {})

def room(request, order_id):
return render(request, 'chatroom.html', {
'order_id': order_id
})
Loading

0 comments on commit 89c5f7a

Please sign in to comment.