notifications

This commit is contained in:
Egor Matveev 2022-05-15 11:05:46 +03:00
parent 138c3fa6e8
commit 4bc1981ef6
11 changed files with 205 additions and 43 deletions

View File

@ -356,6 +356,50 @@ services:
parallelism: 1 parallelism: 1
order: start-first order: start-first
telegram_sender:
image: mathwave/sprint-repo:sprint
networks:
- net
command: ./manage.py telegram_sender
environment:
DB_HOST: "postgres"
FS_HOST: "storage"
RABBIT_HOST: "rabbitmq"
REDIS_HOST: "redis"
DB_PASSWORD: $DB_PASSWORD
DEBUG: $DEBUG
TELEGRAM_TOKEN: $TELEGRAM_TOKEN
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
email_sender:
image: mathwave/sprint-repo:sprint
networks:
- net
command: ./manage.py email_sender
environment:
DB_HOST: "postgres"
FS_HOST: "storage"
RABBIT_HOST: "rabbitmq"
REDIS_HOST: "redis"
DB_PASSWORD: $DB_PASSWORD
DEBUG: $DEBUG
EMAIL_PASSWORD: $EMAIL_PASSWORD
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
apply-languages: apply-languages:
image: mathwave/sprint-repo:sprint image: mathwave/sprint-repo:sprint
networks: networks:

View File

@ -360,6 +360,50 @@ services:
parallelism: 1 parallelism: 1
order: start-first order: start-first
telegram_sender:
image: mathwave/sprint-repo:sprint
networks:
- net
command: ./manage.py telegram_sender
environment:
DB_HOST: "postgres"
FS_HOST: "storage"
RABBIT_HOST: "rabbitmq"
REDIS_HOST: "redis"
DB_PASSWORD: $DB_PASSWORD
DEBUG: $DEBUG
TELEGRAM_TOKEN: $TELEGRAM_TOKEN
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
email_sender:
image: mathwave/sprint-repo:sprint
networks:
- net
command: ./manage.py email_sender
environment:
DB_HOST: "postgres"
FS_HOST: "storage"
RABBIT_HOST: "rabbitmq"
REDIS_HOST: "redis"
DB_PASSWORD: $DB_PASSWORD
DEBUG: $DEBUG
EMAIL_PASSWORD: $EMAIL_PASSWORD
deploy:
mode: replicated
replicas: 2
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
apply-languages: apply-languages:
image: mathwave/sprint-repo:sprint image: mathwave/sprint-repo:sprint
networks: networks:

View File

@ -10,8 +10,8 @@ from django.utils import timezone
from Checker.models import Checker from Checker.models import Checker
from FileStorage.sync import synchronized_method from FileStorage.sync import synchronized_method
from Main.models import Solution, SolutionFile, ExtraFile, Progress from Main.models import Solution, SolutionFile, ExtraFile, Progress
from SprintLib.queue import send_to_queue
from SprintLib.utils import generate_token from SprintLib.utils import generate_token
from SprintLib.queue import notify as notification
def get_dynamic(request): def get_dynamic(request):
@ -92,13 +92,10 @@ def notify(request):
solution = Solution.objects.get(id=request.GET['solution_id']) solution = Solution.objects.get(id=request.GET['solution_id'])
if checker.set != solution.set: if checker.set != solution.set:
return JsonResponse({"status": "incorrect solution"}, status=403) return JsonResponse({"status": "incorrect solution"}, status=403)
notification( send_to_queue("notification", {
solution.user, "type": "solution",
"solution_result", "solution_id": solution.id
f"Задача: {solution.task.name}\n" })
f"Результат: {solution.result}\n"
f"Очки решения: {Progress.by_solution(solution).score}\n"
f"Текущий рейтинг: {solution.user.userinfo.rating}")
return JsonResponse({"status": True}) return JsonResponse({"status": True})
except ObjectDoesNotExist: except ObjectDoesNotExist:
return JsonResponse({"status": "incorrect token"}, status=403) return JsonResponse({"status": "incorrect token"}, status=403)

View File

@ -1,10 +1,10 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Q from django.db.models import Q
from SprintLib.queue import notify
from Main.models import Friendship from Main.models import Friendship
from SprintLib.BaseView import BaseView from SprintLib.BaseView import BaseView
from SprintLib.language import languages from SprintLib.language import languages
from SprintLib.queue import send_to_queue
from SprintLib.utils import delete_file, write_bytes from SprintLib.utils import delete_file, write_bytes
@ -46,17 +46,26 @@ class AccountView(BaseView):
).first() ).first()
if friendship is None: if friendship is None:
Friendship.objects.create(from_user=self.request.user, to_user=self.context["account"]) Friendship.objects.create(from_user=self.request.user, to_user=self.context["account"])
notify(self.context['account'], 'friends', f"Пользователь {self.request.user.username} хочет добавить тебя в друзья") send_to_queue("notification", {
"type": "friends_add",
"from_user": self.request.user.id,
"to_user": self.context["account"].id
})
elif friendship.verified or friendship.from_user == self.request.user: elif friendship.verified or friendship.from_user == self.request.user:
friendship.delete() friendship.delete()
else: else:
if self.request.POST["to_do"] == "yes": if self.request.POST["to_do"] == "yes":
friendship.verified = True friendship.verified = True
friendship.save() friendship.save()
notify(self.context['account'], 'friends', f"Пользователь {self.request.user.username} добавил тебя в друзья")
else: else:
friendship.delete() friendship.delete()
notify(self.context['account'], 'friends', f"Пользователь {self.request.user.username} отклонил твою заявку") send_to_queue("notification", {
"type": "friends_accept",
"from_user": self.request.user.id,
"to_user": self.context['account'].id,
"accepted": self.request.POST['to_do'] == 'yes'
})
return "/account?username=" + self.request.GET["username"] return "/account?username=" + self.request.GET["username"]
def post_upload_photo(self): def post_upload_photo(self):

View File

@ -5,7 +5,7 @@ from django.contrib.auth.models import User
from Sprint import settings from Sprint import settings
from SprintLib.BaseView import BaseView from SprintLib.BaseView import BaseView
from SprintLib.queue import notify from SprintLib.queue import send_to_queue
class SendCodeView(BaseView): class SendCodeView(BaseView):
@ -23,7 +23,10 @@ class SendCodeView(BaseView):
print(f"Отправлен код для {username}", code) print(f"Отправлен код для {username}", code)
user.userinfo.code = code user.userinfo.code = code
user.userinfo.save() user.userinfo.save()
notify(user, "any", "Код для входа в сервис: " + str(code)) send_to_queue("telegram", {
"chat_id": user.userinfo.telegram_chat_id,
"text": "Код для входа в сервис: " + str(code)
})
return {"success": True, "message": "Код отправлен"} return {"success": True, "message": "Код отправлен"}
def post_check(self): def post_check(self):

View File

@ -147,6 +147,14 @@ RABBIT_PORT = 5672
FS_HOST = "http://" + os.getenv("FS_HOST", "127.0.0.1") FS_HOST = "http://" + os.getenv("FS_HOST", "127.0.0.1")
FS_PORT = 5555 FS_PORT = 5555
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.yandex.ru"
EMAIL_HOST_USER = "sprint.no-reply@yandex.ru"
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD")
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
# Authentication backends # Authentication backends
AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",) AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",)

View File

@ -56,11 +56,3 @@ class MessagingSupport(BaseCommand):
channel.start_consuming() channel.start_consuming()
except AMQPConnectorException: except AMQPConnectorException:
print("connection to rabbit failed: reconnecting") print("connection to rabbit failed: reconnecting")
def notify(user: User, notification_type: str, text: str):
send_to_queue("notifications", {
'user_id': user.id,
'type': notification_type,
'text': text,
})

View File

@ -5,7 +5,7 @@ from tempfile import TemporaryDirectory
from Main.models.progress import Progress from Main.models.progress import Progress
from Sprint.settings import CONSTS from Sprint.settings import CONSTS
from SprintLib.queue import notify, send_to_queue from SprintLib.queue import send_to_queue
from SprintLib.utils import Timer from SprintLib.utils import Timer
@ -111,14 +111,10 @@ class BaseTester:
def notify(self): def notify(self):
self.solution.user.userinfo.refresh_from_db() self.solution.user.userinfo.refresh_from_db()
notify( send_to_queue("notification", {
self.solution.user, "type": "solution",
"solution_result", "solution_id": self.solution.id
f"Задача: {self.solution.task.name}\n" })
f"Результат: {self.solution.result}\n"
f"Очки решения: {Progress.by_solution(self.solution).score}\n"
f"Текущий рейтинг: {self.solution.user.userinfo.rating}",
)
def cleanup(self): def cleanup(self):
self.solution.save() self.solution.save()

View File

@ -0,0 +1,20 @@
from django.core.mail import send_mail
from Sprint.settings import EMAIL_HOST_USER
from SprintLib.queue import MessagingSupport
class Command(MessagingSupport):
help = "starts file email sender"
queue_name = "email"
def process(self, payload: dict):
subject = payload['subject']
message = payload['message']
email = payload['email']
send_mail(
subject,
message,
EMAIL_HOST_USER,
[email]
)

View File

@ -1,20 +1,53 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from SprintLib.queue import MessagingSupport from Main.models import Solution, Progress
from daemons.management.commands.bot import bot from SprintLib.queue import MessagingSupport, send_to_queue
class Command(MessagingSupport): class Command(MessagingSupport):
help = "starts file notification manager" help = "starts file notification manager"
queue_name = "notifications" queue_name = "notification"
def handle_solution(self, payload):
solution = Solution.objects.get(id=payload["solution_id"])
user = solution.user
if user.userinfo.notification_solution_result:
message = f"Задача: {solution.task.name}\n" \
f"Результат: {solution.result}\n" \
f"Очки решения: {Progress.by_solution(solution).score}\n" \
f"Текущий рейтинг: {solution.user.userinfo.rating}"
if user.userinfo.telegram_chat_id is not None:
yield "telegram", {"chat_id": user.userinfo.telegram_chat_id, "text": message}
if user.email:
yield "email", {"subject": "Тестирование завершено", "message": message, "email": user.email}
def handle_friends_add(self, payload):
user = User.objects.get(id=payload['to_user_id'])
from_user = User.objects.get(id=payload['from_user_id'])
if user.userinfo.notification_friends:
message = f"Пользователь {from_user.username} хочет добавить тебя в друзья"
if user.userinfo.telegram_chat_id:
yield "telegram", {"chat_id": user.userinfo.telegram_chat_id, "text": message}
if user.email:
yield "email", {"subject": "Новая заявка в друзья", "message": message, "email": user.email}
def handle_friends_accept(self, payload):
user = User.objects.get(id=payload['to_user_id'])
from_user = User.objects.get(id=payload['from_user_id'])
if user.userinfo.notification_friends:
if payload['accepted']:
message = f"Пользователь {from_user} одобрил заявку в друзья"
else:
message = f"Пользователь {from_user} отклонил заявку в друзья"
if user.userinfo.telegram_chat_id:
yield "telegram", {"chat_id": user.userinfo.telegram_chat_id, "text": message}
if user.email:
yield "email", {"subject": "Новая заявка в друзья", "message": message, "email": user.email}
def process(self, payload: dict): def process(self, payload: dict):
user = User.objects.get(id=payload['user_id']) notification_type = payload["type"]
notification_type = payload['type'] handler = getattr(self, "handle_" + notification_type, None)
text = payload['text'] if handler is None:
if notification_type == "any" or getattr(user.userinfo, "notification_" + notification_type): raise ValueError(f"Unknown type: {notification_type}")
bot.send_message( for queue, payload in handler(payload):
user.userinfo.telegram_chat_id, send_to_queue(queue, payload)
text,
parse_mode="html",
)

View File

@ -0,0 +1,16 @@
from SprintLib.queue import MessagingSupport
from daemons.management.commands.bot import bot
class Command(MessagingSupport):
help = "starts file telegram sender"
queue_name = "telegram"
def process(self, payload: dict):
chat_id = payload['chat_id']
text = payload['text']
bot.send_message(
int(chat_id),
text,
parse_mode="html",
)