Add telegram functionality to backend. Messages: welcome and order taken.

This commit is contained in:
Reckless_Satoshi 2022-02-21 15:41:36 -08:00
parent 1feebacbc1
commit df320ea4d0
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
8 changed files with 197 additions and 23 deletions

View File

@ -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)'

View File

@ -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

View 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
View 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

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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