AO3-DL/python/mailParser.py

99 lines
3.4 KiB
Python
Executable file

#!/usr/bin/env python3
# This file is part of AOMO, "Archive Of My Own", a collection of Python and PHP
# scripts designed act as an improved local backup system
# for works published on https://archiveofourown.org
#
# Copyright (c) 2025 Cyberpro123, except where otherwise noted.
#
# This project is available under the GNU General Public License (GPL) v2.0,
# available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
# or in the 'LICENSE.md' file that should be distributed alongside this one.
#
# Report issues to https://codeberg.org/Cyberpro123/AOMO
# Contact author at cyberpro123@posteo.com
import datetime
import mailbox
import bs4
import common
def verifyAo3Email(
message: mailbox.Message,
) -> bool:
rightSubject = bool(message["subject"][:5] == "[AO3]")
rightSender = bool(
message["from"] == "Archive of Our Own <do-not-reply@archiveofourown.org>"
)
if rightSubject and rightSender:
return True
return False
def getIdsFromMessage(
message: mailbox.Message,
) -> set:
out = set(())
for part in message.walk():
if part.get_content_type() == "text/html":
body = part.get_payload(decode=True)
soup = bs4.BeautifulSoup(body, "lxml")
hrefs = []
for link in soup.find_all("a"):
hrefs.append(str(link.get("href")))
for i in hrefs:
j = common.getWorkIdFromLink(i)
if j:
out.add(j)
return out
if __name__ == "__main__":
sinceStr = "Specify a time, mail sent before this time will be ignored. Accepts either a date string in YYYY/MM/DD-HH:MM:SS format, representing a literal "
sinceStr += str(
"time, or an integer, representing a certain number of seconds ago (ie 86400 will accept only mail from within the last day)",
)
# autoformatter gave me a 'string too long' warning
common.init(
description="Scans through an email inbox file to detect AO3 subscription notification emails.",
secondOut="WorkID Output",
main=("mailbox", "Location of mailbox file to scan"),
since=sinceStr,
)
del sinceStr
config = common.config
mailFile = common.os.path.abspath(common.os.path.expanduser(common.args.mailbox[0]))
if not common.os.path.exists(mailFile):
raise FileNotFoundError(f"File [{mailFile}] doesn't exist.")
earliestOkTime = datetime.datetime.now().timestamp() - 86400
if common.args.since:
try:
earliestOkTime = datetime.datetime.strptime(
common.args.since, "%Y/%m/%d-%H:%M:%S"
).timestamp()
except ValueError:
try:
earliestOkTime = datetime.datetime.now().timestamp() - int(
common.args.since
)
except ValueError:
common.logging.getLogger(__name__).error(
f"Can't parse argument to --since [{common.args.since}], using default value (1 day ago) instead"
)
ids = set(())
for message in mailbox.mbox(mailFile):
if verifyAo3Email(message=message):
if (
datetime.datetime.strptime(
message["date"], "%a, %d %b %Y %H:%M:%S %z"
).timestamp()
> earliestOkTime
):
for id in getIdsFromMessage(message=message):
ids.add(id)
for workID in sorted(ids):
common.args.batch_out.write(f"{workID}\n")