diff --git a/rc_hip/repapp/mail_interface.py b/rc_hip/repapp/mail_interface.py new file mode 100644 index 0000000..a5a22ec --- /dev/null +++ b/rc_hip/repapp/mail_interface.py @@ -0,0 +1,100 @@ +import os +import re +import logging +from imap_tools import MailBox, AND, MailMessage +from django.core.mail import send_mail +from .models import Guest, Message, Question, Organisator + +logger = logging.getLogger(__name__) + + +def send_message_notification(message: Message, guest: Guest): + """ + Send new message notification. + """ + organizers = [] + for organizer in Organisator.objects.all(): + organizers.append(organizer.mail) + + subject = f'Neue Nachricht von Gast {guest.name} ({guest.mail})' + text = f'Hallo,\n' + f'der Gast {guest.name} ({guest.mail}) hat folgende Nachricht gesendet:\n\n' + f'{message.message}' + # TODO: HTML message + # TODO: add attachments + # TODO: add link to message + + send_mail( + subject=subject, + message=text, + from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""), + recipient_list=organizers, + fail_silently=True + ) + + +def process_message(message: MailMessage, guest: Guest): + """ + Process a valid message from a guest. + """ + content = message.text + if message.html: + content = message.html + + result = re.search('Q#([0-9]+):', message.subject) + if result: + question_pk = result.group(1) + question = Question.objects.filter(pk=question_pk).first() + if question: + if question.device.guest.mail == message.from_: + question.answer = content + question.save() + # TODO: save attachments and add to message + logger.info( + 'Valid answer for Question %s from %s received.' + % (question_pk, message.from_)) + # TODO: send notifications + else: + logger.warning( + 'Answer for Question %s from %s received,' + ' but the question was not for this guest. Message was ignored.' + % (question_pk, message.from_)) + # TODO: send reply that mail address was wrong + else: + logger.warning( + 'Answer for Question %s from %s received,' + ' but there is no such question. Message was ignored.' + % (question_pk, message.from_)) + else: + db_message = Message( + message=content, + guest=guest, + ) + db_message.save() + send_message_notification(message, guest) + + +def process_mails(): + """ + Check for new messages and handle valid messages. + """ + host = os.getenv("DJANGO_EMAIL_HOST", "") + user = os.getenv("DJANGO_EMAIL_HOST_USER", None) + password = os.getenv("DJANGO_EMAIL_HOST_PASSWORD", None) + mailbox = MailBox(host).login(user, password) + messages = mailbox.fetch(criteria=AND(seen=False), + mark_seen=True, bulk=True) + + message_count = 0 + for message in messages: + message_count += 1 + sender = message.from_ + guest = Guest.objects.filter(mail=sender).first() + if guest: + process_message(message, guest) + else: + logger.warning( + 'Mail form unknown sender %s with subject %s ignored.' + % (sender, message.subject)) + + return message_count diff --git a/rc_hip/repapp/views.py b/rc_hip/repapp/views.py index 110556e..e3cfcd1 100644 --- a/rc_hip/repapp/views.py +++ b/rc_hip/repapp/views.py @@ -14,15 +14,15 @@ from django.contrib import messages from django.views import generic from django.urls import reverse_lazy from django.shortcuts import get_object_or_404, render -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponse from django.core.exceptions import PermissionDenied -from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login from django.utils.timezone import now -from repapp.models import CustomUser, Organisator, Reparateur -from .models import Cafe, Device, Guest, OneTimeLogin from .forms import RegisterDevice, RegisterGuest +from .models import (Cafe, Device, Guest, OneTimeLogin, + CustomUser, Organisator, Reparateur) +from . import mail_interface def send_one_time_login_mail(secret, mail, request): @@ -216,11 +216,15 @@ class RegisterDeviceFormView(generic.edit.FormView): device.save() guest = Guest.objects.filter(mail=mail).first() + user = CustomUser.objects.filter(email=mail).first() - if guest: + if guest and user: device.guest = guest device.save() + guest.user = user + guest.save() + send_confirmation_mails(device, guest, cafe, self.request) resp = HttpResponseRedirect( @@ -407,14 +411,15 @@ def cron(request): """ View to trigger automated regular tasks. """ - pass + return HttpResponse('done.') def process_mails(request): """ View to trigger processing of email in inbox. """ - pass + count = mail_interface.process_mails() + return HttpResponse(f'Done. Processed {count} messages.') def one_time_login(request, secret: str): diff --git a/requirements_dev.txt b/requirements_dev.txt index 66f1b07..fce3d99 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -11,3 +11,4 @@ mozilla-django-oidc==3.0.0 djlint==1.23.3 django-import-export==3.2.0 selenium==4.9.0 +imap-tools==1.0.0 diff --git a/requirements_prod.txt b/requirements_prod.txt index 8013a0c..0eb9641 100644 --- a/requirements_prod.txt +++ b/requirements_prod.txt @@ -5,3 +5,4 @@ crispy-bootstrap4==2022.1 crispy-bootstrap5==0.7 mozilla-django-oidc==3.0.0 django-import-export==3.2.0 +imap-tools==1.0.0