mirror of
https://github.com/Reckless-Satoshi/robosats.git
synced 2024-08-16 15:20:55 +03:00
Add telegram functionality to backend. Messages: welcome and order taken.
This commit is contained in:
parent
1feebacbc1
commit
df320ea4d0
@ -40,6 +40,10 @@ ONION_LOCATION = ''
|
||||
ALTERNATIVE_SITE = 'RoboSats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion'
|
||||
ALTERNATIVE_NAME = 'RoboSats Mainnet'
|
||||
|
||||
# Telegram bot token
|
||||
TELEGRAM_TOKEN = ''
|
||||
TELEGRAM_BOT_NAME = ''
|
||||
|
||||
# Lightning node open info, url to amboss and 1ML
|
||||
NETWORK = 'testnet'
|
||||
NODE_ALIAS = '🤖RoboSats⚡(RoboDevs)'
|
||||
|
@ -4,6 +4,7 @@ from api.lightning.node import LNNode
|
||||
from django.db.models import Q
|
||||
|
||||
from api.models import Order, LNPayment, MarketTick, User, Currency
|
||||
from api.messages import Telegram
|
||||
from decouple import config
|
||||
|
||||
from api.tasks import follow_send_payment
|
||||
@ -130,6 +131,7 @@ class Logics:
|
||||
order.expires_at = timezone.now() + timedelta(
|
||||
seconds=Order.t_to_expire[Order.Status.TAK])
|
||||
order.save()
|
||||
Telegram.order_taken(order)
|
||||
return True, None
|
||||
|
||||
def is_buyer(order, user):
|
||||
@ -1051,3 +1053,4 @@ class Logics:
|
||||
user.profile.platform_rating = rating
|
||||
user.profile.save()
|
||||
return True, None
|
||||
|
||||
|
50
api/management/commands/telegram_watcher.py
Normal file
50
api/management/commands/telegram_watcher.py
Normal file
@ -0,0 +1,50 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from api.models import Profile
|
||||
from api.messages import Telegram
|
||||
from decouple import config
|
||||
import requests
|
||||
import time
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
help = "Polls telegram /getUpdates method"
|
||||
rest = 3 # seconds between consecutive polls
|
||||
|
||||
bot_token = config('TELEGRAM_TOKEN')
|
||||
updates_url = f'https://api.telegram.org/bot{bot_token}/getUpdates'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Infinite loop to check for telegram updates.
|
||||
If it finds a new user (/start), enables it's taker found
|
||||
notification and sends a 'Hey {username} {order_id}' message back"""
|
||||
|
||||
offset = 0
|
||||
while True:
|
||||
time.sleep(self.rest)
|
||||
|
||||
params = {'offset' : offset + 1 , 'timeout' : 5}
|
||||
print(params)
|
||||
response = requests.get(self.updates_url, params=params).json()
|
||||
if len(list(response['result'])) == 0:
|
||||
continue
|
||||
for result in response['result']:
|
||||
text = result['message']['text']
|
||||
splitted_text = text.split(' ')
|
||||
if splitted_text[0] == '/start':
|
||||
token = splitted_text[-1]
|
||||
print(token)
|
||||
try :
|
||||
profile = Profile.objects.get(telegram_token=token)
|
||||
except:
|
||||
print(f'No profile with token {token}')
|
||||
continue
|
||||
profile.telegram_chat_id = result['message']['from']['id']
|
||||
profile.telegram_lang_code = result['message']['from']['language_code']
|
||||
Telegram.welcome(profile.user)
|
||||
profile.telegram_enabled = True
|
||||
profile.save()
|
||||
|
||||
offset = response['result'][-1]['update_id']
|
||||
|
||||
|
63
api/messages.py
Normal file
63
api/messages.py
Normal file
@ -0,0 +1,63 @@
|
||||
from decouple import config
|
||||
from secrets import token_urlsafe
|
||||
from api.models import Order
|
||||
import requests
|
||||
|
||||
class Telegram():
|
||||
''' Simple telegram messages by requesting to API'''
|
||||
|
||||
def get_context(user):
|
||||
"""returns context needed to enable TG notifications"""
|
||||
context = {}
|
||||
if user.profile.telegram_enabled :
|
||||
context['tg_enabled'] = True
|
||||
else:
|
||||
context['tg_enabled'] = False
|
||||
|
||||
if user.profile.telegram_token == None:
|
||||
user.profile.telegram_token = token_urlsafe(15)
|
||||
user.profile.save()
|
||||
|
||||
context['tg_token'] = user.profile.telegram_token
|
||||
context['tg_bot_name'] = config("TELEGRAM_BOT_NAME")
|
||||
|
||||
return context
|
||||
|
||||
def send_message(user, text):
|
||||
""" sends a message to a user with telegram notifications enabled"""
|
||||
|
||||
bot_token=config('TELEGRAM_TOKEN')
|
||||
|
||||
chat_id = user.profile.telegram_chat_id
|
||||
message_url = f'https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&text={text}'
|
||||
|
||||
response = requests.get(message_url).json()
|
||||
print(response)
|
||||
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def welcome(cls, user):
|
||||
lang = user.profile.telegram_lang_code
|
||||
order = Order.objects.get(maker=user)
|
||||
print(str(order.id))
|
||||
if lang == 'es':
|
||||
text = f'Hola {user.username}, te enviaré un mensaje cuando tu orden con ID {str(order.id)} haya sido tomada.'
|
||||
else:
|
||||
text = f"Hey {user.username}, I will send you a message when someone takes your order with ID {str(order.id)}."
|
||||
cls.send_message(user, text)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def order_taken(cls, order):
|
||||
user = order.maker
|
||||
lang = user.profile.telegram_lang_code
|
||||
taker_nick = order.taker.username
|
||||
site = config('HOST_NAME')
|
||||
if lang == 'es':
|
||||
text = f'Tu orden con ID {order.id} ha sido tomada por {taker_nick}!🎉 Visita http://{site}/order/{order.id} para continuar.'
|
||||
else:
|
||||
text = f'Your order with ID {order.id} was taken by {taker_nick}!🎉 Visit http://{site}/order/{order.id} to proceed with the trade.'
|
||||
|
||||
cls.send_message(user, text)
|
||||
return
|
@ -250,12 +250,11 @@ class Order(models.Model):
|
||||
) # unique = True, a taker can only take one order
|
||||
maker_last_seen = models.DateTimeField(null=True, default=None, blank=True)
|
||||
taker_last_seen = models.DateTimeField(null=True, default=None, blank=True)
|
||||
maker_asked_cancel = models.BooleanField(
|
||||
default=False, null=False
|
||||
) # When collaborative cancel is needed and one partner has cancelled.
|
||||
taker_asked_cancel = models.BooleanField(
|
||||
default=False, null=False
|
||||
) # When collaborative cancel is needed and one partner has cancelled.
|
||||
|
||||
# When collaborative cancel is needed and one partner has cancelled.
|
||||
maker_asked_cancel = models.BooleanField(default=False, null=False)
|
||||
taker_asked_cancel = models.BooleanField(default=False, null=False)
|
||||
|
||||
is_fiat_sent = models.BooleanField(default=False, null=False)
|
||||
|
||||
# in dispute
|
||||
@ -372,7 +371,7 @@ class Profile(models.Model):
|
||||
default=None,
|
||||
validators=[validate_comma_separated_integer_list],
|
||||
blank=True,
|
||||
) # Will only store latest ratings
|
||||
) # Will only store latest rating
|
||||
avg_rating = models.DecimalField(
|
||||
max_digits=4,
|
||||
decimal_places=1,
|
||||
@ -382,7 +381,30 @@ class Profile(models.Model):
|
||||
MaxValueValidator(100)],
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# Used to deep link telegram chat in case telegram notifications are enabled
|
||||
telegram_token = models.CharField(
|
||||
max_length=20,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
telegram_chat_id = models.BigIntegerField(
|
||||
null=True,
|
||||
default=None,
|
||||
blank=True
|
||||
)
|
||||
telegram_enabled = models.BooleanField(
|
||||
default=False,
|
||||
null=False
|
||||
)
|
||||
telegram_lang_code = models.CharField(
|
||||
max_length=4,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
telegram_welcomed = models.BooleanField(
|
||||
default=False,
|
||||
null=False
|
||||
)
|
||||
# Disputes
|
||||
num_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||
lost_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||
|
28
api/utils.py
28
api/utils.py
@ -3,6 +3,7 @@ from decouple import config
|
||||
import numpy as np
|
||||
|
||||
from api.models import Order
|
||||
from secrets import token_urlsafe
|
||||
|
||||
market_cache = {}
|
||||
|
||||
@ -86,8 +87,6 @@ def get_commit_robosats():
|
||||
|
||||
|
||||
premium_percentile = {}
|
||||
|
||||
|
||||
@ring.dict(premium_percentile, expire=300)
|
||||
def compute_premium_percentile(order):
|
||||
|
||||
@ -106,3 +105,28 @@ def compute_premium_percentile(order):
|
||||
|
||||
rates = np.array(rates)
|
||||
return round(np.sum(rates < order_rate) / len(rates), 2)
|
||||
|
||||
|
||||
def get_telegram_context(user):
|
||||
"""returns context needed to enable TG notifications"""
|
||||
context = {}
|
||||
if user.profile.telegram_enabled :
|
||||
context['tg_enabled'] = True
|
||||
else:
|
||||
context['tg_enabled'] = False
|
||||
|
||||
if user.profile.telegram_token == None:
|
||||
user.profile.telegram_token = token_urlsafe(15)
|
||||
|
||||
context['tg_token'] = user.profile.telegram_token
|
||||
context['tg_bot_name'] = config("TELEGRAM_BOT_NAME")
|
||||
|
||||
return context
|
||||
|
||||
def send_telegram_notification(user, text):
|
||||
bot_token=config('TELEGRAM_TOKEN')
|
||||
chat_id = user.profile.telegram_chat_id
|
||||
message_url = f'https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&text={text}'
|
||||
response = requests.get(message_url).json()
|
||||
print(response)
|
||||
return
|
21
api/views.py
21
api/views.py
@ -9,10 +9,11 @@ from rest_framework.response import Response
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
|
||||
from .models import LNPayment, MarketTick, Order, Currency
|
||||
from .logics import Logics
|
||||
from .utils import get_lnd_version, get_commit_robosats, compute_premium_percentile
|
||||
from api.serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
|
||||
from api.models import LNPayment, MarketTick, Order, Currency
|
||||
from api.logics import Logics
|
||||
from api.messages import Telegram
|
||||
from api.utils import get_lnd_version, get_commit_robosats, compute_premium_percentile
|
||||
|
||||
from .nick_generator.nick_generator import NickGenerator
|
||||
from robohash import Robohash
|
||||
@ -182,15 +183,17 @@ class OrderView(viewsets.ViewSet):
|
||||
|
||||
# 3.b If order is between public and WF2
|
||||
if order.status >= Order.Status.PUB and order.status < Order.Status.WF2:
|
||||
data["price_now"], data[
|
||||
"premium_now"] = Logics.price_and_premium_now(order)
|
||||
data["price_now"], data["premium_now"] = Logics.price_and_premium_now(order)
|
||||
|
||||
# 3. c) If maker and Public, add num robots in book, premium percentile and num similar orders.
|
||||
# 3. c) If maker and Public, add num robots in book, premium percentile
|
||||
# num similar orders, and maker information to enable telegram notifications.
|
||||
if data["is_maker"] and order.status == Order.Status.PUB:
|
||||
data["premium_percentile"] = compute_premium_percentile(order)
|
||||
data["num_similar_orders"] = len(
|
||||
Order.objects.filter(currency=order.currency,
|
||||
status=Order.Status.PUB))
|
||||
# Adds/generate telegram token and whether it is enabled
|
||||
data = {**data,**Telegram.get_context(request.user)}
|
||||
|
||||
# 4) Non participants can view details (but only if PUB)
|
||||
elif not data["is_participant"] and order.status != Order.Status.PUB:
|
||||
@ -518,8 +521,7 @@ class UserView(APIView):
|
||||
# Sends the welcome back message, only if created +3 mins ago
|
||||
if request.user.date_joined < (timezone.now() -
|
||||
timedelta(minutes=3)):
|
||||
context[
|
||||
"found"] = "We found your Robot avatar. Welcome back!"
|
||||
context["found"] = "We found your Robot avatar. Welcome back!"
|
||||
return Response(context, status=status.HTTP_202_ACCEPTED)
|
||||
else:
|
||||
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion change)
|
||||
@ -612,7 +614,6 @@ class BookView(ListAPIView):
|
||||
|
||||
return Response(book_data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class InfoView(ListAPIView):
|
||||
|
||||
def get(self, request):
|
||||
|
@ -20,7 +20,6 @@ services:
|
||||
DEVELOPMENT: 1
|
||||
volumes:
|
||||
- .:/usr/src/robosats
|
||||
- /mnt/development/database:/usr/src/database
|
||||
- /mnt/development/lnd:/lnd
|
||||
network_mode: service:tor
|
||||
|
||||
@ -38,7 +37,6 @@ services:
|
||||
command: python3 manage.py clean_orders
|
||||
volumes:
|
||||
- .:/usr/src/robosats
|
||||
- /mnt/development/database:/usr/src/database
|
||||
network_mode: service:tor
|
||||
|
||||
follow-invoices:
|
||||
@ -51,7 +49,16 @@ services:
|
||||
command: python3 manage.py follow_invoices
|
||||
volumes:
|
||||
- .:/usr/src/robosats
|
||||
- /mnt/development/database:/usr/src/database
|
||||
- /mnt/development/lnd:/lnd
|
||||
network_mode: service:tor
|
||||
|
||||
telegram-watcher:
|
||||
build: .
|
||||
container_name: tg-dev
|
||||
restart: always
|
||||
command: python3 manage.py telegram_watcher
|
||||
volumes:
|
||||
- .:/usr/src/robosats
|
||||
- /mnt/development/lnd:/lnd
|
||||
network_mode: service:tor
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user