Merge pull request #1032 from pheerai/feature-mailActionCustomTag

Feature mail action custom tag
This commit is contained in:
Trenton Holmes 2022-06-02 09:46:34 -07:00 committed by GitHub
commit 4db3f366ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 0 deletions

View File

@ -161,6 +161,9 @@ These are as follows:
will not consume flagged mails. will not consume flagged mails.
* **Move to folder:** Moves consumed mails out of the way so that paperless wont * **Move to folder:** Moves consumed mails out of the way so that paperless wont
consume them again. consume them again.
* **Add custom Tag:** Adds a custom tag to mails with consumed documents (the IMAP
standard calls these "keywords"). Paperless will not consume mails already tagged.
Not all mail servers support this feature!
.. caution:: .. caution::

View File

@ -62,6 +62,17 @@ class FlagMailAction(BaseMailAction):
M.flag(message_uids, [MailMessageFlags.FLAGGED], True) M.flag(message_uids, [MailMessageFlags.FLAGGED], True)
class TagMailAction(BaseMailAction):
def __init__(self, parameter):
self.keyword = parameter
def get_criteria(self):
return {"no_keyword": self.keyword}
def post_consume(self, M: MailBox, message_uids, parameter):
M.flag(message_uids, [self.keyword], True)
def get_rule_action(rule): def get_rule_action(rule):
if rule.action == MailRule.MailAction.FLAG: if rule.action == MailRule.MailAction.FLAG:
return FlagMailAction() return FlagMailAction()
@ -71,6 +82,8 @@ def get_rule_action(rule):
return MoveMailAction() return MoveMailAction()
elif rule.action == MailRule.MailAction.MARK_READ: elif rule.action == MailRule.MailAction.MARK_READ:
return MarkReadMailAction() return MarkReadMailAction()
elif rule.action == MailRule.MailAction.TAG:
return TagMailAction(rule.action_parameter)
else: else:
raise NotImplementedError("Unknown action.") # pragma: nocover raise NotImplementedError("Unknown action.") # pragma: nocover

View File

@ -0,0 +1,28 @@
# Generated by Django 4.0.4 on 2022-05-29 13:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paperless_mail", "0014_alter_mailrule_action"),
]
operations = [
migrations.AlterField(
model_name="mailrule",
name="action",
field=models.PositiveIntegerField(
choices=[
(1, "Delete"),
(2, "Move to specified folder"),
(3, "Mark as read, don't process read mails"),
(4, "Flag the mail, don't process flagged mails"),
(5, "Tag the mail with specified tag, don't process tagged mails"),
],
default=3,
verbose_name="action",
),
),
]

View File

@ -65,6 +65,7 @@ class MailRule(models.Model):
MOVE = 2, _("Move to specified folder") MOVE = 2, _("Move to specified folder")
MARK_READ = 3, _("Mark as read, don't process read mails") MARK_READ = 3, _("Mark as read, don't process read mails")
FLAG = 4, _("Flag the mail, don't process flagged mails") FLAG = 4, _("Flag the mail, don't process flagged mails")
TAG = 5, _("Tag the mail with specified tag, don't process tagged mails")
class TitleSource(models.IntegerChoices): class TitleSource(models.IntegerChoices):
FROM_SUBJECT = 1, _("Use subject as title") FROM_SUBJECT = 1, _("Use subject as title")

View File

@ -96,6 +96,10 @@ class BogusMailBox(ContextManager):
if "UNFLAGGED" in criteria: if "UNFLAGGED" in criteria:
msg = filter(lambda m: not m.flagged, msg) msg = filter(lambda m: not m.flagged, msg)
if "UNKEYWORD" in criteria:
tag = criteria[criteria.index("UNKEYWORD") + 1].strip("'")
msg = filter(lambda m: "processed" not in m.flags, msg)
return list(msg) return list(msg)
def delete(self, uid_list): def delete(self, uid_list):
@ -109,6 +113,9 @@ class BogusMailBox(ContextManager):
message.flagged = value message.flagged = value
if flag == MailMessageFlags.SEEN: if flag == MailMessageFlags.SEEN:
message.seen = value message.seen = value
if flag == "processed":
message._raw_flag_data.append(f"+FLAGS (processed)".encode())
MailMessage.flags.fget.cache_clear()
def move(self, uid_list, folder): def move(self, uid_list, folder):
if folder == "spam": if folder == "spam":
@ -130,6 +137,7 @@ def create_message(
from_: str = "noone@mail.com", from_: str = "noone@mail.com",
seen: bool = False, seen: bool = False,
flagged: bool = False, flagged: bool = False,
processed: bool = False,
) -> MailMessage: ) -> MailMessage:
email_msg = email.message.EmailMessage() email_msg = email.message.EmailMessage()
# TODO: This does NOT set the UID # TODO: This does NOT set the UID
@ -175,6 +183,9 @@ def create_message(
imap_msg.seen = seen imap_msg.seen = seen
imap_msg.flagged = flagged imap_msg.flagged = flagged
if processed:
imap_msg._raw_flag_data.append(f"+FLAGS (processed)".encode())
MailMessage.flags.fget.cache_clear()
return imap_msg return imap_msg
@ -217,6 +228,7 @@ class TestMail(DirectoriesMixin, TestCase):
body="cables", body="cables",
seen=True, seen=True,
flagged=False, flagged=False,
processed=False,
), ),
) )
self.bogus_mailbox.messages.append( self.bogus_mailbox.messages.append(
@ -225,6 +237,7 @@ class TestMail(DirectoriesMixin, TestCase):
body="from my favorite electronic store", body="from my favorite electronic store",
seen=False, seen=False,
flagged=True, flagged=True,
processed=True,
), ),
) )
self.bogus_mailbox.messages.append( self.bogus_mailbox.messages.append(
@ -571,6 +584,29 @@ class TestMail(DirectoriesMixin, TestCase):
self.assertEqual(len(self.bogus_mailbox.messages), 2) self.assertEqual(len(self.bogus_mailbox.messages), 2)
self.assertEqual(len(self.bogus_mailbox.messages_spam), 1) self.assertEqual(len(self.bogus_mailbox.messages_spam), 1)
def test_handle_mail_account_tag(self):
account = MailAccount.objects.create(
name="test",
imap_server="",
username="admin",
password="secret",
)
_ = MailRule.objects.create(
name="testrule",
account=account,
action=MailRule.MailAction.TAG,
action_parameter="processed",
)
self.assertEqual(len(self.bogus_mailbox.messages), 3)
self.assertEqual(self.async_task.call_count, 0)
self.assertEqual(len(self.bogus_mailbox.fetch("UNKEYWORD processed", False)), 2)
self.mail_account_handler.handle_mail_account(account)
self.assertEqual(self.async_task.call_count, 2)
self.assertEqual(len(self.bogus_mailbox.fetch("UNKEYWORD processed", False)), 0)
self.assertEqual(len(self.bogus_mailbox.messages), 3)
def test_error_login(self): def test_error_login(self):
account = MailAccount.objects.create( account = MailAccount.objects.create(
name="test", name="test",