108 lines
4.0 KiB
Python
108 lines
4.0 KiB
Python
import email
|
|
import imaplib
|
|
import json
|
|
import logging
|
|
from email.header import decode_header
|
|
|
|
from profiles.models import UserProfile
|
|
from scrobbles.models import Scrobble
|
|
from scrobbles.scrobblers import email_scrobble_board_game
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def import_scrobbles_from_imap() -> list[Scrobble]:
|
|
"""For all user profiles with IMAP creds, check inbox for scrobbleable email attachments."""
|
|
scrobbles_created: list[Scrobble] = []
|
|
|
|
active_profiles = UserProfile.objects.filter(imap_auto_import=True)
|
|
logger.info(
|
|
"Starting import of scrobbles from IMAP",
|
|
extra={"active_profiles": active_profiles},
|
|
)
|
|
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") # TODO configure this in profile
|
|
|
|
# Search for unseen emails
|
|
status, messages = mail.search(None, "UnSeen")
|
|
if status != "OK":
|
|
logger.info("IMAP status not OK", extra={"status": status})
|
|
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:
|
|
message = email.message_from_bytes(msg_data[0][1])
|
|
logger.info(
|
|
"Processing email message", extra={"email_msg": message}
|
|
)
|
|
except IndexError:
|
|
logger.info("No email message data found")
|
|
return
|
|
|
|
# Decode subject safely
|
|
subject, encoding = decode_header(message["Subject"])[0]
|
|
if isinstance(subject, bytes):
|
|
subject = subject.decode(encoding or "utf-8")
|
|
if "live activity now!" in subject:
|
|
print("Found a Garmin email")
|
|
|
|
for part in message.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
|
|
parsed_json = {}
|
|
if filename.lower().endswith(".bgsplay"):
|
|
# TODO Pull this out into a parse_pgsplay function
|
|
try:
|
|
parsed_json = json.loads(
|
|
file_data.decode("utf-8")
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
"Failed to parse JSON file",
|
|
extra={"filename": filename, "error": e},
|
|
)
|
|
|
|
if not parsed_json:
|
|
logger.info(
|
|
"No JSON found in BG Stats file",
|
|
extra={"filename": filename},
|
|
)
|
|
continue
|
|
|
|
scrobbles_created = email_scrobble_board_game(
|
|
parsed_json, profile.user_id
|
|
)
|
|
|
|
mail.logout()
|
|
|
|
if scrobbles_created:
|
|
logger.info(
|
|
f"Creating {len(scrobbles_created)} new scrobbles",
|
|
extra={"scrobbles_created": scrobbles_created},
|
|
)
|
|
return scrobbles_created
|
|
logger.info(f"No new scrobbles found in IMAP folders")
|
|
return []
|