Skip to content

Commit

Permalink
Merge pull request #1356 from RoboSats/new-notifications
Browse files Browse the repository at this point in the history
New notifications
  • Loading branch information
KoalaSat authored Jul 13, 2024
2 parents e9a49d3 + c960d19 commit 886956c
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 20 deletions.
2 changes: 2 additions & 0 deletions api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def maker_wins(self, request, queryset):
f"Dispute of order {order.id} solved successfully on favor of the maker",
messages.SUCCESS,
)
send_notification.delay(order_id=order.id, message="dispute_closed")

else:
self.message_user(
Expand Down Expand Up @@ -248,6 +249,7 @@ def taker_wins(self, request, queryset):
f"Dispute of order {order.id} solved successfully on favor of the taker",
messages.SUCCESS,
)
send_notification.delay(order_id=order.id, message="dispute_closed")

else:
self.message_user(
Expand Down
19 changes: 19 additions & 0 deletions api/migrations/0048_alter_order_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.0.6 on 2024-06-29 14:07

import api.models.order
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0047_notification'),
]

operations = [
migrations.AlterField(
model_name='order',
name='reference',
field=models.UUIDField(default=api.models.order.custom_uuid, editable=False),
),
]
8 changes: 4 additions & 4 deletions api/models/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.utils import timezone
from api.tasks import send_notification

if config("TESTING", cast=bool, default=False):
import random
Expand Down Expand Up @@ -91,10 +92,7 @@ class ExpiryReasons(models.IntegerChoices):
decimal_places=2,
default=0,
null=True,
validators=[
MinValueValidator(Decimal(-100)),
MaxValueValidator(Decimal(999))
],
validators=[MinValueValidator(Decimal(-100)), MaxValueValidator(Decimal(999))],
blank=True,
)
# explicit
Expand Down Expand Up @@ -352,6 +350,8 @@ def update_status(self, new_status):
self.log(
f"Order state went from {old_status}: <i>{Order.Status(old_status).label}</i> to {new_status}: <i>{Order.Status(new_status).label}</i>"
)
if new_status == Order.Status.FAI:
send_notification.delay(order_id=self.id, message="lightning_failed")


@receiver(pre_delete, sender=Order)
Expand Down
6 changes: 1 addition & 5 deletions api/models/robot.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
from pathlib import Path

from django.conf import settings
from django.contrib.auth.models import User
from django.core.validators import validate_comma_separated_integer_list
from django.db import models
from django.db.models.signals import post_save, pre_delete
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.html import mark_safe


class Robot(models.Model):
Expand Down
44 changes: 44 additions & 0 deletions api/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,47 @@ def coordinator_cancelled(self, order):
title = f"🛠️ Your order with ID {order.id} has been cancelled by the coordinator {config('COORDINATOR_ALIAS', cast=str, default='NoAlias')} for the upcoming maintenance stop."
self.send_message(order, order.maker.robot, title)
return

def dispute_closed(self, order):
lang = order.maker.robot.telegram_lang_code
if order.status == Order.Status.MLD:
# Maker lost dispute
looser = order.maker
winner = order.taker
elif order.status == Order.Status.TLD:
# Taker lost dispute
looser = order.taker
winner = order.maker

lang = looser.robot.telegram_lang_code
if lang == "es":
title = f"⚖️ Hey {looser.username}, has perdido la disputa en la orden con ID {str(order.id)}."
else:
title = f"⚖️ Hey {looser.username}, you lost the dispute on your order with ID {str(order.id)}."
self.send_message(order, looser.robot, title)

lang = winner.robot.telegram_lang_code
if lang == "es":
title = f"⚖️ Hey {winner.username}, has ganado la disputa en la orden con ID {str(order.id)}."
else:
title = f"⚖️ Hey {winner.username}, you won the dispute on your order with ID {str(order.id)}."
self.send_message(order, winner.robot, title)

return

def lightning_failed(self, order):
lang = order.maker.robot.telegram_lang_code
if order.type == Order.Types.BUY:
buyer = order.maker
else:
buyer = order.taker

if lang == "es":
title = f"⚡❌ Hey {buyer.username}, el pago lightning en la order con ID {str(order.id)} ha fallado."
description = "Intentalo de nuevo con una nueva factura o con otra wallet."
else:
title = f"⚡❌ Hey {buyer.username}, the lightning payment on your order with ID {str(order.id)} failed."
description = "Try again with a new invoice or from another wallet."

self.send_message(order, buyer.robot, title, description)
return
9 changes: 8 additions & 1 deletion api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,9 +491,16 @@ class Meta:


class ListNotificationSerializer(serializers.ModelSerializer):
status = serializers.SerializerMethodField(
help_text="The `status` of the order when the notification was trigered",
)

class Meta:
model = Notification
fields = ("title", "description", "order_id")
fields = ("title", "description", "order_id", "status", "created_at")

def get_status(self, notification) -> int:
return notification.order.status


class OrderPublicSerializer(serializers.ModelSerializer):
Expand Down
6 changes: 6 additions & 0 deletions api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,10 @@ def send_notification(order_id=None, chat_message_id=None, message=None):
elif message == "coordinator_cancelled":
notifications.coordinator_cancelled(order)

elif message == "dispute_closed":
notifications.dispute_closed(order)

elif message == "lightning_failed":
notifications.lightning_failed(order)

return
3 changes: 0 additions & 3 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,10 +761,7 @@ def get(self, request, format=None):
notification_data = []
for notification in queryset:
data = self.serializer_class(notification).data
data["title"] = str(notification.title)
data["description"] = str(notification.description)
data["order_id"] = notification.order.id

notification_data.append(data)

return Response(notification_data, status=status.HTTP_200_OK)
Expand Down
14 changes: 7 additions & 7 deletions docker-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ version: '3.9'
services:
bitcoind:
image: ruimarinho/bitcoin-core:${BITCOIND_VERSION:-24.0.1}-alpine
container_name: btc
container_name: test-btc
restart: always
ports:
- "8000:8000"
Expand Down Expand Up @@ -50,7 +50,7 @@ services:

coordinator-LND:
image: lightninglabs/lnd:${LND_VERSION:-v0.17.0-beta}
container_name: coordinator-LND
container_name: test-coordinator-LND
restart: always
volumes:
- bitcoin:/root/.bitcoin/
Expand Down Expand Up @@ -83,7 +83,7 @@ services:
coordinator-CLN:
image: elementsproject/lightningd:${CLN_VERSION:-v24.05}
restart: always
container_name: coordinator-CLN
container_name: test-coordinator-CLN
environment:
LIGHTNINGD_NETWORK: 'regtest'
volumes:
Expand All @@ -97,7 +97,7 @@ services:

robot-LND:
image: lightninglabs/lnd:${LND_VERSION:-v0.17.0-beta}
container_name: robot-LND
container_name: test-robot-LND
restart: always
volumes:
- bitcoin:/root/.bitcoin/
Expand Down Expand Up @@ -129,7 +129,7 @@ services:

redis:
image: redis:${REDIS_VERSION:-7.2.1}-alpine
container_name: redis
container_name: test-redis
restart: always
volumes:
- redisdata:/data
Expand All @@ -141,7 +141,7 @@ services:
args:
DEVELOPMENT: True
image: backend-image
container_name: coordinator
container_name: test-coordinator
restart: always
environment:
DEVELOPMENT: True
Expand Down Expand Up @@ -171,7 +171,7 @@ services:

postgres:
image: postgres:${POSTGRES_VERSION:-14.2}-alpine
container_name: sql
container_name: test-sql
restart: always
environment:
POSTGRES_PASSWORD: 'example'
Expand Down
8 changes: 8 additions & 0 deletions docs/assets/schemas/api-latest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1106,8 +1106,16 @@ components:
order_id:
type: integer
readOnly: true
status:
type: integer
readOnly: true
description: The `status` of the order when the notification was trigered
created_at:
type: string
format: date-time
required:
- order_id
- status
ListOrder:
type: object
properties:
Expand Down
54 changes: 54 additions & 0 deletions tests/test_trade_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,60 @@ def test_order_expires_after_only_maker_messaged(self):
f"⚖️ Hey {data['taker_nick']}, a dispute has been opened on your order with ID {str(trade.order_id)}.",
)

# def test_dispute_closed_maker_wins(self):
# trade = Trade(self.client)
# trade.publish_order()
# trade.take_order()
# trade.lock_taker_bond()
# trade.lock_escrow(trade.taker_index)
# trade.submit_payout_invoice(trade.maker_index)

# # Admin resolves dispute

# trade.clean_orders()

# maker_headers = trade.get_robot_auth(trade.maker_index)
# response = self.client.get(reverse("notifications"), **maker_headers)
# self.assertResponse(response)
# notifications_data = list(response.json())
# self.assertEqual(notifications_data[0]["order_id"], trade.order_id)
# self.assertEqual(
# notifications_data[0]["title"],
# f"⚖️ Hey {data['maker_nick']}, you won the dispute on your order with ID {str(trade.order_id)}."
# )
# taker_headers = trade.get_robot_auth(trade.taker_index)
# response = self.client.get(reverse("notifications"), **taker_headers)
# self.assertResponse(response)
# notifications_data = list(response.json())
# self.assertEqual(notifications_data[0]["order_id"], trade.order_id)
# self.assertEqual(
# notifications_data[0]["title"],
# f"⚖️ Hey {data['taker_nick']}, you lost the dispute on your order with ID {str(trade.order_id)}."
# )

def test_lightning_payment_failed(self):
trade = Trade(self.client)
trade.publish_order()
trade.take_order()
trade.lock_taker_bond()
trade.lock_escrow(trade.taker_index)
trade.submit_payout_invoice(trade.maker_index)

trade.change_order_status(Order.Status.FAI)

trade.clean_orders()

maker_headers = trade.get_robot_auth(trade.maker_index)
maker_nick = read_file(f"tests/robots/{trade.maker_index}/nickname")
response = self.client.get(reverse("notifications"), **maker_headers)
self.assertResponse(response)
notifications_data = list(response.json())
self.assertEqual(notifications_data[0]["order_id"], trade.order_id)
self.assertEqual(
notifications_data[0]["title"],
f"⚡❌ Hey {maker_nick}, the lightning payment on your order with ID {str(trade.order_id)} failed.",
)

def test_withdraw_reward_after_unilateral_cancel(self):
"""
Tests withdraw rewards as taker after maker cancels order unilaterally
Expand Down
6 changes: 6 additions & 0 deletions tests/utils/trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,9 @@ def expire_order(self):
order = Order.objects.get(id=self.order_id)
order.expires_at = datetime.now()
order.save()

@patch("api.tasks.send_notification.delay", send_notification)
def change_order_status(self, status):
# Change order expiry to now
order = Order.objects.get(id=self.order_id)
order.update_status(status)

0 comments on commit 886956c

Please sign in to comment.