mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-25 16:45:05 +00:00
Merge pull request #439 from eikek/mail-subject-filter
Mail subject filter
This commit is contained in:
commit
b0bd7e417f
@ -33,7 +33,9 @@ case class ScanMailboxArgs(
|
|||||||
// set a filter for files when importing archives
|
// set a filter for files when importing archives
|
||||||
fileFilter: Option[Glob],
|
fileFilter: Option[Glob],
|
||||||
// set a list of tags to apply to new item
|
// set a list of tags to apply to new item
|
||||||
tags: Option[List[String]]
|
tags: Option[List[String]],
|
||||||
|
// a glob filter for the mail subject
|
||||||
|
subjectFilter: Option[Glob]
|
||||||
)
|
)
|
||||||
|
|
||||||
object ScanMailboxArgs {
|
object ScanMailboxArgs {
|
||||||
|
@ -142,7 +142,7 @@ object ScanMailboxTask {
|
|||||||
_ <- Kleisli.liftF(ctx.logger.info(s"Processing folder $name"))
|
_ <- Kleisli.liftF(ctx.logger.info(s"Processing folder $name"))
|
||||||
folder <- requireFolder(a)(name)
|
folder <- requireFolder(a)(name)
|
||||||
search <- searchMails(a)(folder)
|
search <- searchMails(a)(folder)
|
||||||
headers <- Kleisli.liftF(filterMessageIds(search.mails))
|
headers <- Kleisli.liftF(filterSubjects(search.mails).flatMap(filterMessageIds))
|
||||||
_ <- headers.traverse(handleOne(ctx.args, a, upload))
|
_ <- headers.traverse(handleOne(ctx.args, a, upload))
|
||||||
} yield ScanResult(name, search.mails.size, search.count - search.mails.size)
|
} yield ScanResult(name, search.mails.size, search.count - search.mails.size)
|
||||||
|
|
||||||
@ -178,6 +178,26 @@ object ScanMailboxTask {
|
|||||||
} yield mails
|
} yield mails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def filterSubjects(headers: Vector[MailHeader]): F[Vector[MailHeader]] =
|
||||||
|
ctx.args.subjectFilter match {
|
||||||
|
case Some(sf) =>
|
||||||
|
def check(mh: MailHeader): F[Option[MailHeader]] =
|
||||||
|
if (sf.matches(mh.subject))
|
||||||
|
ctx.logger.debug(
|
||||||
|
s"Including mail '${mh.subject}', it matches the filter."
|
||||||
|
) *> Option(mh).pure[F]
|
||||||
|
else
|
||||||
|
ctx.logger.debug(
|
||||||
|
s"Excluding mail '${mh.subject}', it doesn't match the filter."
|
||||||
|
) *> (None: Option[MailHeader]).pure[F]
|
||||||
|
ctx.logger.info(
|
||||||
|
s"Filtering mails on subject using filter: ${sf.asString}"
|
||||||
|
) *> headers.traverseFilter(check)
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
ctx.logger.debug("Not matching on subjects. No filter given") *> headers.pure[F]
|
||||||
|
}
|
||||||
|
|
||||||
def filterMessageIds(headers: Vector[MailHeader]): F[Vector[MailHeader]] =
|
def filterMessageIds(headers: Vector[MailHeader]): F[Vector[MailHeader]] =
|
||||||
NonEmptyList.fromFoldable(headers.flatMap(_.messageId)) match {
|
NonEmptyList.fromFoldable(headers.flatMap(_.messageId)) match {
|
||||||
case Some(nl) =>
|
case Some(nl) =>
|
||||||
|
@ -3503,7 +3503,12 @@ components:
|
|||||||
$ref: "#/components/schemas/StringList"
|
$ref: "#/components/schemas/StringList"
|
||||||
fileFilter:
|
fileFilter:
|
||||||
description: |
|
description: |
|
||||||
A glob to filter attachments to import.
|
A glob to filter attachments to import by file name.
|
||||||
|
type: string
|
||||||
|
format: glob
|
||||||
|
subjectFilter:
|
||||||
|
description: |
|
||||||
|
A glob to filter attachments to import by subject.
|
||||||
type: string
|
type: string
|
||||||
format: glob
|
format: glob
|
||||||
|
|
||||||
|
@ -115,7 +115,8 @@ object ScanMailboxRoutes {
|
|||||||
settings.direction,
|
settings.direction,
|
||||||
settings.itemFolder,
|
settings.itemFolder,
|
||||||
settings.fileFilter,
|
settings.fileFilter,
|
||||||
settings.tags.map(_.items)
|
settings.tags.map(_.items),
|
||||||
|
settings.subjectFilter
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -137,7 +138,7 @@ object ScanMailboxRoutes {
|
|||||||
task.id,
|
task.id,
|
||||||
task.enabled,
|
task.enabled,
|
||||||
conn.getOrElse(Ident.unsafe("")),
|
conn.getOrElse(Ident.unsafe("")),
|
||||||
task.args.folders, //folders
|
task.args.folders,
|
||||||
task.timer,
|
task.timer,
|
||||||
task.args.receivedSince.map(_.hours.toInt),
|
task.args.receivedSince.map(_.hours.toInt),
|
||||||
task.args.targetFolder,
|
task.args.targetFolder,
|
||||||
@ -145,6 +146,7 @@ object ScanMailboxRoutes {
|
|||||||
task.args.direction,
|
task.args.direction,
|
||||||
task.args.itemFolder,
|
task.args.itemFolder,
|
||||||
task.args.tags.map(StringList.apply),
|
task.args.tags.map(StringList.apply),
|
||||||
task.args.fileFilter
|
task.args.fileFilter,
|
||||||
|
task.args.subjectFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ type alias Model =
|
|||||||
, tagModel : Comp.Dropdown.Model Tag
|
, tagModel : Comp.Dropdown.Model Tag
|
||||||
, existingTags : List String
|
, existingTags : List String
|
||||||
, fileFilter : Maybe String
|
, fileFilter : Maybe String
|
||||||
|
, subjectFilter : Maybe String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -94,6 +95,7 @@ type Msg
|
|||||||
| GetTagResp (Result Http.Error TagList)
|
| GetTagResp (Result Http.Error TagList)
|
||||||
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
|
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
|
||||||
| SetFileFilter String
|
| SetFileFilter String
|
||||||
|
| SetSubjectFilter String
|
||||||
|
|
||||||
|
|
||||||
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
|
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
|
||||||
@ -135,6 +137,7 @@ initWith flags s =
|
|||||||
Maybe.map .items s.tags
|
Maybe.map .items s.tags
|
||||||
|> Maybe.withDefault []
|
|> Maybe.withDefault []
|
||||||
, fileFilter = s.fileFilter
|
, fileFilter = s.fileFilter
|
||||||
|
, subjectFilter = s.subjectFilter
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Api.getImapSettings flags "" ConnResp
|
[ Api.getImapSettings flags "" ConnResp
|
||||||
@ -184,6 +187,7 @@ init flags =
|
|||||||
, tagModel = Util.Tag.makeDropdownModel
|
, tagModel = Util.Tag.makeDropdownModel
|
||||||
, existingTags = []
|
, existingTags = []
|
||||||
, fileFilter = Nothing
|
, fileFilter = Nothing
|
||||||
|
, subjectFilter = Nothing
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Api.getImapSettings flags "" ConnResp
|
[ Api.getImapSettings flags "" ConnResp
|
||||||
@ -228,6 +232,7 @@ makeSettings model =
|
|||||||
, schedule = Data.CalEvent.makeEvent timer
|
, schedule = Data.CalEvent.makeEvent timer
|
||||||
, itemFolder = model.itemFolderId
|
, itemFolder = model.itemFolderId
|
||||||
, fileFilter = model.fileFilter
|
, fileFilter = model.fileFilter
|
||||||
|
, subjectFilter = model.subjectFilter
|
||||||
, tags =
|
, tags =
|
||||||
case Comp.Dropdown.getSelected model.tagModel of
|
case Comp.Dropdown.getSelected model.tagModel of
|
||||||
[] ->
|
[] ->
|
||||||
@ -586,6 +591,12 @@ update flags msg model =
|
|||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SetSubjectFilter str ->
|
||||||
|
( { model | subjectFilter = Util.Maybe.fromString str }
|
||||||
|
, NoAction
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- View
|
--- View
|
||||||
@ -653,13 +664,6 @@ view extraClasses settings model =
|
|||||||
[ text "The folders to go through"
|
[ text "The folders to go through"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, Html.map ReceivedHoursMsg
|
|
||||||
(Comp.IntField.viewWithInfo
|
|
||||||
"Select mails newer than `now - receivedHours`"
|
|
||||||
model.receivedHours
|
|
||||||
"field"
|
|
||||||
model.receivedHoursModel
|
|
||||||
)
|
|
||||||
, div [ class "field" ]
|
, div [ class "field" ]
|
||||||
[ label [] [ text "Target folder" ]
|
[ label [] [ text "Target folder" ]
|
||||||
, input
|
, input
|
||||||
@ -688,6 +692,74 @@ view extraClasses settings model =
|
|||||||
, text " is not set."
|
, text " is not set."
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
, div [ class "ui dividing header" ]
|
||||||
|
[ text "Filter"
|
||||||
|
]
|
||||||
|
, Html.map ReceivedHoursMsg
|
||||||
|
(Comp.IntField.viewWithInfo
|
||||||
|
"Select mails newer than `now - receivedHours`"
|
||||||
|
model.receivedHours
|
||||||
|
"field"
|
||||||
|
model.receivedHoursModel
|
||||||
|
)
|
||||||
|
, div
|
||||||
|
[ class "field"
|
||||||
|
]
|
||||||
|
[ label [] [ text "File Filter" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, onInput SetFileFilter
|
||||||
|
, placeholder "File Filter"
|
||||||
|
, model.fileFilter
|
||||||
|
|> Maybe.withDefault ""
|
||||||
|
|> value
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, div [ class "small-info" ]
|
||||||
|
[ text "Specify a file glob to filter attachments. For example, to only extract pdf files: "
|
||||||
|
, code []
|
||||||
|
[ text "*.pdf"
|
||||||
|
]
|
||||||
|
, text ". If you want to include the mail body, allow html files or "
|
||||||
|
, code []
|
||||||
|
[ text "mail.html"
|
||||||
|
]
|
||||||
|
, text ". Globs can be combined via OR, like this: "
|
||||||
|
, code []
|
||||||
|
[ text "*.pdf|mail.html"
|
||||||
|
]
|
||||||
|
, text ". No file filter defaults to "
|
||||||
|
, code []
|
||||||
|
[ text "*"
|
||||||
|
]
|
||||||
|
, text " that includes all"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div
|
||||||
|
[ class "field"
|
||||||
|
]
|
||||||
|
[ label [] [ text "Subject Filter" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, onInput SetSubjectFilter
|
||||||
|
, placeholder "Subject Filter"
|
||||||
|
, model.subjectFilter
|
||||||
|
|> Maybe.withDefault ""
|
||||||
|
|> value
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, div [ class "small-info" ]
|
||||||
|
[ text "Specify a file glob to filter mails by subject. For example: "
|
||||||
|
, code []
|
||||||
|
[ text "*Scanned Document*"
|
||||||
|
]
|
||||||
|
, text ". No file filter defaults to "
|
||||||
|
, code []
|
||||||
|
[ text "*"
|
||||||
|
]
|
||||||
|
, text " that includes all"
|
||||||
|
]
|
||||||
|
]
|
||||||
, div [ class "ui dividing header" ]
|
, div [ class "ui dividing header" ]
|
||||||
[ text "Metadata"
|
[ text "Metadata"
|
||||||
]
|
]
|
||||||
@ -763,39 +835,6 @@ disappear then.
|
|||||||
[ text "Choose tags that should be applied to items."
|
[ text "Choose tags that should be applied to items."
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, div
|
|
||||||
[ class "field"
|
|
||||||
]
|
|
||||||
[ label [] [ text "File Filter" ]
|
|
||||||
, input
|
|
||||||
[ type_ "text"
|
|
||||||
, onInput SetFileFilter
|
|
||||||
, placeholder "File Filter"
|
|
||||||
, model.fileFilter
|
|
||||||
|> Maybe.withDefault ""
|
|
||||||
|> value
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
, div [ class "small-info" ]
|
|
||||||
[ text "Specify a file glob to filter attachments. For example, to only extract pdf files: "
|
|
||||||
, code []
|
|
||||||
[ text "*.pdf"
|
|
||||||
]
|
|
||||||
, text ". If you want to include the mail body, allow html files or "
|
|
||||||
, code []
|
|
||||||
[ text "mail.html"
|
|
||||||
]
|
|
||||||
, text ". Globs can be combined via OR, like this: "
|
|
||||||
, code []
|
|
||||||
[ text "*.pdf|mail.html"
|
|
||||||
]
|
|
||||||
, text "No file filter defaults to "
|
|
||||||
, code []
|
|
||||||
[ text "*"
|
|
||||||
]
|
|
||||||
, text " that includes all"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
, div [ class "ui dividing header" ]
|
, div [ class "ui dividing header" ]
|
||||||
[ text "Schedule"
|
[ text "Schedule"
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user