mirror of
https://codeberg.org/Cyberpro123/AO3-DL.git
synced 2026-07-18 04:05:57 +00:00
99 lines
3.4 KiB
Python
Executable file
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")
|