|
@@ -0,0 +1,98 @@
|
|
|
+import json
|
|
|
+import imaplib
|
|
|
+import email
|
|
|
+from email.header import decode_header
|
|
|
+from django.core.files.base import ContentFile
|
|
|
+from profiles.models import UserProfile
|
|
|
+from scrobbles.models import Scrobble
|
|
|
+from scrobbles.scrobblers import email_scrobble_board_game
|
|
|
+import logging
|
|
|
+
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
+
|
|
|
+def process_scrobbles_from_imap() -> list[Scrobble] | None:
|
|
|
+ """For all user profiles with IMAP creds, check inbox for scrobbleable email attachments."""
|
|
|
+ scrobbles_to_create: list[Scrobble] = []
|
|
|
+
|
|
|
+ active_profiles = UserProfile.objects.filter(imap_auto_import=True)
|
|
|
+ for profile in active_profiles:
|
|
|
+ logger.info(
|
|
|
+ "Importing scrobbles from IMAP for user",
|
|
|
+ extra={"user_id": profile.user_id},
|
|
|
+ )
|
|
|
+ mail = imaplib.IMAP4_SSL(profile.imap_url)
|
|
|
+ mail.login(profile.imap_user, profile.imap_pass)
|
|
|
+ mail.select("INBOX")
|
|
|
+
|
|
|
+ # Search for unseen emails
|
|
|
+ status, messages = mail.search(None, "(UNSEEN)")
|
|
|
+ if status != "OK":
|
|
|
+ return
|
|
|
+
|
|
|
+ for uid in messages[0].split():
|
|
|
+ status, msg_data = mail.fetch(uid, "(RFC822)")
|
|
|
+ if status != "OK":
|
|
|
+ logger.info("IMAP status not OK", extra={"status": status})
|
|
|
+ continue
|
|
|
+
|
|
|
+ try:
|
|
|
+ msg = email.message_from_bytes(msg_data[0][1])
|
|
|
+ logger.info("Processing email message", extra={"msg": msg})
|
|
|
+ except IndexError:
|
|
|
+ logger.info("No email message data found")
|
|
|
+ return
|
|
|
+
|
|
|
+ for part in msg.walk():
|
|
|
+ if part.get_content_disposition() == "attachment":
|
|
|
+ filename = part.get_filename()
|
|
|
+ if filename:
|
|
|
+ # Decode the filename if necessary
|
|
|
+ decoded_name, encoding = decode_header(filename)[0]
|
|
|
+ if isinstance(decoded_name, bytes):
|
|
|
+ filename = decoded_name.decode(encoding or "utf-8")
|
|
|
+
|
|
|
+ file_data = part.get_payload(decode=True)
|
|
|
+
|
|
|
+ parsed_json = ""
|
|
|
+
|
|
|
+ # Try parsing JSON if applicable
|
|
|
+ if filename.lower().endswith(".bgsplay"):
|
|
|
+ try:
|
|
|
+ parsed_json = json.loads(
|
|
|
+ file_data.decode("utf-8")
|
|
|
+ )
|
|
|
+ scrobbles_to_create.append(
|
|
|
+ email_scrobble_board_game(
|
|
|
+ parsed_json, profile.user_id
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ # You might want to log this
|
|
|
+ print(
|
|
|
+ f"Failed to parse JSON from {filename}: {e}"
|
|
|
+ )
|
|
|
+
|
|
|
+ # Avoid duplicates
|
|
|
+ if not EmailAttachment.objects.filter(
|
|
|
+ email_uid=uid.decode(), filename=filename
|
|
|
+ ).exists():
|
|
|
+ attachment = EmailAttachment(
|
|
|
+ email_uid=uid.decode(), filename=filename
|
|
|
+ )
|
|
|
+ attachment.file.save(
|
|
|
+ filename, ContentFile(file_data)
|
|
|
+ )
|
|
|
+ attachment.save()
|
|
|
+
|
|
|
+ mail.logout()
|
|
|
+
|
|
|
+ if scrobbles_to_create:
|
|
|
+ logger.info(
|
|
|
+ f"Creating {len(scrobbles_to_create)} new scrobbles",
|
|
|
+ extra={"scrobbles_to_create": scrobbles_to_create},
|
|
|
+ )
|
|
|
+ created_scrobbles = Scrobble.objects.bulk_create(scrobbles_to_create)
|
|
|
+ return created_scrobbles
|
|
|
+ logger.info(f"No new scrobbles found in IMAP folders")
|