-**Bitcoin mainnet:**
-- Tor: robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion
-- Url: robosats.com (Coming soon)
-- Version: v0.1.0-mvp
+### 🔗 **Bitcoin Mainnet**
+- 🧅 **TOR URL:** [**RoboSats**6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion](http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion) ( Open with [Tor Browser](https://www.torproject.org/download/))
+- Clearnet URL: [unsafe.robosats.com](https://unsafe.robosats.com) (not recommended!)
+- Version: v0.1.0 MVP (+++)
-**Bitcoin testnet:**
-- Tor: robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
-- Url: testnet.robosats.com (Coming soon)
-- Latest commit.
+*⚠️ Always use [Tor Browser](https://www.torproject.org/download/) and .onion for best anonymity. The Clearnet URL redirects to a third party Tor2web service. Your privacy cannot be guaranteed to be respected. Use only to check around the app, never use for trading!⚠️*
-*Always use [Tor Browser](https://www.torproject.org/download/) and .onion for best anonymity.*
+You can also use RoboSats in Testnet:
+- TOR URL: [RoboTestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion](http://robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion)
+- Clearnet URL: [unsafe.testnet.robosats.com](https://unsafe.testnet.robosats.com)
## How to use it
diff --git a/api/admin.py b/api/admin.py
index 6a7ae746..8c46902d 100644
--- a/api/admin.py
+++ b/api/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
from django_admin_relation_links import AdminChangeLinksMixin
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import UserAdmin
-from .models import Order, LNPayment, Profile, MarketTick, Currency
+from api.models import Order, LNPayment, Profile, MarketTick, Currency
admin.site.unregister(Group)
admin.site.unregister(User)
diff --git a/chat/admin.py b/chat/admin.py
index 8c38f3f3..af4f55a7 100644
--- a/chat/admin.py
+++ b/chat/admin.py
@@ -1,3 +1,20 @@
from django.contrib import admin
-
+from django_admin_relation_links import AdminChangeLinksMixin
+from chat.models import ChatRoom
# Register your models here.
+
+
+@admin.register(ChatRoom)
+class UserProfileAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
+ list_display = (
+ "id",
+ "order_link",
+ "maker_link",
+ "taker_link",
+ "maker_connected",
+ "taker_connected",
+ "maker_connect_date",
+ "taker_connect_date",
+ "room_group_name",
+ )
+ change_links = ["order","maker","taker"]
\ No newline at end of file
diff --git a/chat/consumers.py b/chat/consumers.py
index 23dd9e9b..c641814b 100644
--- a/chat/consumers.py
+++ b/chat/consumers.py
@@ -1,57 +1,136 @@
from channels.generic.websocket import AsyncWebsocketConsumer
-from api.logics import Logics
+from channels.db import database_sync_to_async
from api.models import Order
+from chat.models import ChatRoom
import json
-
class ChatRoomConsumer(AsyncWebsocketConsumer):
+ @database_sync_to_async
+ def allow_in_chatroom(self):
+ order = Order.objects.get(id=self.order_id)
+ if not (order.maker == self.user or order.taker == self.user):
+ print("Not allowed in this chat")
+ return False
+ return True
+
+ @database_sync_to_async
+ def save_connect_user(self):
+ '''Creates or updates the ChatRoom object'''
+
+ order = Order.objects.get(id=self.order_id)
+
+ if order.maker == self.user:
+ ChatRoom.objects.update_or_create(
+ id=self.order_id,
+ order=order,
+ room_group_name=self.room_group_name,
+ defaults={
+ "maker": self.user,
+ "maker_connected": True,
+ }
+ )
+
+ elif order.taker == self.user:
+ ChatRoom.objects.update_or_create(
+ id=self.order_id,
+ order=order,
+ room_group_name=self.room_group_name,
+ defaults={
+ "taker": self.user,
+ "taker_connected": True,
+ }
+ )
+
+ return None
+
+ @database_sync_to_async
+ def save_disconnect_user(self):
+ '''Creates or updates the ChatRoom object'''
+
+ order = Order.objects.get(id=self.order_id)
+ if order.maker == self.user:
+ ChatRoom.objects.update_or_create(
+ id=self.order_id,
+ defaults={
+ "maker_connected": False
+ }
+ )
+ elif order.taker == self.user:
+ ChatRoom.objects.update_or_create(
+ id=self.order_id,
+ defaults={
+ "taker_connected": False
+ }
+ )
+ return None
+
+ @database_sync_to_async
+ def is_peer_connected(self):
+ '''Creates or updates the ChatRoom object'''
+
+ chatroom = ChatRoom.objects.get(id=self.order_id)
+
+ if chatroom.maker == self.user:
+ return chatroom.taker_connected
+
+ if chatroom.taker == self.user:
+ return chatroom.maker_connected
+
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)
+ allowed = await self.allow_in_chatroom()
- # # 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
+ if allowed:
+ await self.save_connect_user()
+ await self.channel_layer.group_add(self.room_group_name,
+ self.channel_name)
- await self.channel_layer.group_add(self.room_group_name,
- self.channel_name)
-
- await self.accept()
+ await self.accept()
async def disconnect(self, close_code):
+ await self.save_disconnect_user()
await self.channel_layer.group_discard(self.room_group_name,
self.channel_name)
+ await self.channel_layer.group_send(
+ self.room_group_name,
+ {
+ "type": "chatroom_message",
+ "message": 'peer-disconnected',
+ "nick": self.scope["user"].username,
+ "peer_connected": False,
+ },
+ )
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
- nick = text_data_json["nick"]
-
+
+ peer_connected = await self.is_peer_connected()
await self.channel_layer.group_send(
self.room_group_name,
{
"type": "chatroom_message",
"message": message,
- "nick": nick,
+ "nick": self.scope["user"].username,
+ "peer_connected": peer_connected,
},
)
async def chatroom_message(self, event):
message = event["message"]
nick = event["nick"]
+ peer_connected = event["peer_connected"]
await self.send(text_data=json.dumps({
"message": message,
"user_nick": nick,
+ "peer_connected": peer_connected,
}))
pass
diff --git a/chat/models.py b/chat/models.py
index 71a83623..31a3df91 100644
--- a/chat/models.py
+++ b/chat/models.py
@@ -1,3 +1,43 @@
from django.db import models
+from api.models import User, Order
-# Create your models here.
+
+class ChatRoom(models.Model):
+ '''
+ Simple ChatRoom model. Needed to facilitate communication: Is my counterpart in the room?
+ '''
+
+ id = models.PositiveBigIntegerField(primary_key=True, null=False,default=None, blank=True)
+ order = models.ForeignKey(
+ Order,
+ related_name="order",
+ on_delete=models.SET_NULL,
+ null=True,
+ default=None)
+ maker = models.ForeignKey(
+ User,
+ related_name="chat_maker",
+ on_delete=models.SET_NULL,
+ null=True,
+ default=None)
+ taker = models.ForeignKey(
+ User,
+ related_name="chat_taker",
+ on_delete=models.SET_NULL,
+ null=True,
+ default=None,
+ blank=True,
+ )
+
+ maker_connected = models.BooleanField(default=False, null=False)
+ taker_connected = models.BooleanField(default=False, null=False)
+
+ maker_connect_date = models.DateTimeField(auto_now_add=True)
+ taker_connect_date = models.DateTimeField(auto_now_add=True)
+
+ room_group_name = models.CharField(
+ max_length=50,
+ null=True,
+ default=None,
+ blank=True,
+ )
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 80748d5a..5e3b0159 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -6583,6 +6583,11 @@
"resolve": "^1.9.0"
}
},
+ "reconnecting-websocket": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz",
+ "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng=="
+ },
"regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 8eb9727b..fe1c1a06 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -38,6 +38,7 @@
"react-qr-reader": "^2.2.1",
"react-responsive": "^9.0.0-beta.6",
"react-router-dom": "^5.2.0",
+ "reconnecting-websocket": "^4.4.0",
"websocket": "^1.0.34"
}
}
diff --git a/frontend/src/components/BottomBar.js b/frontend/src/components/BottomBar.js
index 5f9ad639..63f63b73 100644
--- a/frontend/src/components/BottomBar.js
+++ b/frontend/src/components/BottomBar.js
@@ -315,11 +315,11 @@ export default class BottomBar extends Component {