@wirecat/mail-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/README.md +41 -10
- package/dist/bin/mail.js +2 -2
- package/dist/bundle/{all-profiles-GR5JXYEQ.js → all-profiles-GBFTKUHA.js} +9 -9
- package/dist/bundle/{anthropic-REBXE65F.js → anthropic-PTSCWUG6.js} +6 -6
- package/dist/bundle/{app-server-QJ7ICQXZ.js → app-server-5VJ2WCKJ.js} +46 -46
- package/dist/bundle/{bun-WAX4QHW5.js → bun-2KRUBPIN.js} +2 -2
- package/dist/bundle/{bun-sqlite-5S7TQ4SG.js → bun-sqlite-ELSRSJBM.js} +5 -5
- package/dist/bundle/{chunk-5CXAH4Y6.js → chunk-3KY7DA5A.js} +428 -434
- package/dist/bundle/{chunk-7JCRQX56.js → chunk-4PKHZQIM.js} +8 -8
- package/dist/bundle/{chunk-QXOSGGKS.js → chunk-56PI2BRH.js} +1 -1
- package/dist/bundle/{chunk-LFRHIXCF.js → chunk-5AQKM47C.js} +3 -3
- package/dist/bundle/{chunk-S677WGOQ.js → chunk-5KHJUYHQ.js} +8 -8
- package/dist/bundle/{chunk-IBYTYIYO.js → chunk-6MV2TBDB.js} +57 -54
- package/dist/bundle/{chunk-M6BDHZON.js → chunk-AGX7NR3S.js} +26 -13
- package/dist/bundle/{chunk-ZD3WUGR2.js → chunk-B337KHJ5.js} +4 -4
- package/dist/bundle/{chunk-5BVIWP3S.js → chunk-BM445GYF.js} +2 -2
- package/dist/bundle/{chunk-WNI5BZFB.js → chunk-BVIHT7KR.js} +9 -9
- package/dist/bundle/{chunk-6EO7GVYJ.js → chunk-DA46WQEA.js} +1 -1
- package/dist/bundle/{chunk-2MRECWJZ.js → chunk-DV4RSEAH.js} +6 -6
- package/dist/bundle/{chunk-ESNNQA7D.js → chunk-ES4PWHBJ.js} +28 -28
- package/dist/bundle/{chunk-KIDF77MA.js → chunk-FIQOBLHS.js} +1 -1
- package/dist/bundle/{chunk-TI6VBZKJ.js → chunk-FUB3X25H.js} +4 -4
- package/dist/bundle/{chunk-EL554DV2.js → chunk-H4VFIH5W.js} +3 -3
- package/dist/bundle/{chunk-7A7BHABA.js → chunk-J4C32YKH.js} +1 -1
- package/dist/bundle/{chunk-MMDCL42A.js → chunk-J5MYH4L5.js} +12 -6
- package/dist/bundle/{chunk-FSUNGHRI.js → chunk-JFJTDL22.js} +47 -46
- package/dist/bundle/{chunk-SWJAG5IE.js → chunk-JIQAFFPV.js} +2 -2
- package/dist/bundle/{chunk-2U4IANVV.js → chunk-JKA2AICL.js} +2 -2
- package/dist/bundle/{dist-KJOAJ6NF.js → chunk-JKLQHPED.js} +16 -116
- package/dist/bundle/{chunk-J3QXTBD2.js → chunk-JLRMSNPX.js} +18 -18
- package/dist/bundle/{chunk-4BMNP2FT.js → chunk-K5JUROGN.js} +1 -1
- package/dist/bundle/{chunk-7F3NDIYL.js → chunk-KUQYWIVR.js} +6 -6
- package/dist/bundle/{chunk-DXDQOZ2P.js → chunk-LWTG5UOF.js} +2 -2
- package/dist/bundle/{chunk-YKPBC3NL.js → chunk-M7V3GSJQ.js} +1 -1
- package/dist/bundle/chunk-MD2J3POF.js +52 -0
- package/dist/bundle/{chunk-MQUJEQTU.js → chunk-NCWO2FNC.js} +2 -2
- package/dist/bundle/{chunk-BLS5YG77.js → chunk-NOU6MWEM.js} +2 -2
- package/dist/bundle/{chunk-LQ37ALDJ.js → chunk-NXJLC2WK.js} +8 -8
- package/dist/bundle/{chunk-F3NYA4KN.js → chunk-NZB4WGQS.js} +6 -6
- package/dist/bundle/{chunk-2FXVBCA3.js → chunk-O555ZEQR.js} +4 -4
- package/dist/bundle/{chunk-7XNPSIWD.js → chunk-O7VK7CJX.js} +2 -2
- package/dist/bundle/{chunk-JJBQPA7D.js → chunk-OHV5RDFE.js} +4 -4
- package/dist/bundle/{chunk-BFJV4FWJ.js → chunk-P5B7IFOG.js} +2 -2
- package/dist/bundle/{chunk-5INWLYBD.js → chunk-PBYDT3CG.js} +13 -3
- package/dist/bundle/{chunk-AFCGOZR7.js → chunk-PHPC72DP.js} +7 -7
- package/dist/bundle/{chunk-B5WSIWY5.js → chunk-PKU6V4EQ.js} +17 -17
- package/dist/bundle/{chunk-P7P5MNAU.js → chunk-QZBT6BOU.js} +1 -1
- package/dist/bundle/{chunk-EB3R2PV3.js → chunk-RAZOSYEO.js} +23 -23
- package/dist/bundle/{chunk-IWDOGGMY.js → chunk-RDSAI6DC.js} +398 -293
- package/dist/bundle/{chunk-PJCTWPJB.js → chunk-SAZJDKEI.js} +8 -8
- package/dist/bundle/{chunk-QX7RCLRS.js → chunk-SQLCCKEL.js} +1 -1
- package/dist/bundle/{chunk-VXAZZGXE.js → chunk-TKJDGAHT.js} +23 -23
- package/dist/bundle/{chunk-6TY3DGCC.js → chunk-TPNC5VCJ.js} +4 -4
- package/dist/bundle/{chunk-6GE3EADU.js → chunk-UAXBLHZK.js} +2 -2
- package/dist/bundle/{chunk-SQ7KOBRA.js → chunk-VEAP5RVI.js} +10 -8
- package/dist/bundle/{chunk-2YQNBIDQ.js → chunk-VF24ZAPX.js} +4 -4
- package/dist/bundle/{chunk-LNS27BTB.js → chunk-WCL3WBH5.js} +3 -3
- package/dist/bundle/{chunk-4XJ4AM3K.js → chunk-WGK4AIRL.js} +12 -12
- package/dist/bundle/{chunk-VDAIJM5L.js → chunk-XERTHQ6D.js} +88 -34
- package/dist/bundle/{chunk-YRGBZZUW.js → chunk-XZGLY2LU.js} +14 -14
- package/dist/bundle/{chunk-WNCCYHRV.js → chunk-Y7JKWQC2.js} +96 -94
- package/dist/bundle/{chunk-LOA4X6JW.js → chunk-YBJMBPL7.js} +6 -6
- package/dist/bundle/{chunk-5FK6RD4M.js → chunk-ZMMIIBTM.js} +73 -57
- package/dist/bundle/{chunk-HCMUGUAG.js → chunk-ZPGLWMNH.js} +3 -3
- package/dist/bundle/{chunk-CEAANMJ2.js → chunk-ZRYFEEXZ.js} +3 -3
- package/dist/bundle/command-contract-NZF4B5OR.js +34 -0
- package/dist/bundle/{decode-D7BDVIVB.js → decode-HVXDCQ63.js} +5 -5
- package/dist/bundle/{dispatch-EUKKT3PD.js → dispatch-GDAH4JHS.js} +8 -8
- package/dist/bundle/dist-43DZXMBL.js +134 -0
- package/dist/bundle/{echarts-IVRX7XHP.js → echarts-LZ74IXHG.js} +2 -2
- package/dist/bundle/{embed-X527RBTG.js → embed-OQF5U2C6.js} +6 -6
- package/dist/bundle/{embedder-F4PSJS6S.js → embedder-OCWQAWKO.js} +9 -9
- package/dist/bundle/{failure-OAIC5VAP.js → failure-6COJAO7T.js} +2 -2
- package/dist/bundle/{first-run-M5CIX6NA.js → first-run-MTZPUPGA.js} +11 -11
- package/dist/bundle/first-screen-4PBS6QK5.js +32 -0
- package/dist/bundle/{instructions-UX36BT5F.js → instructions-M6F4FP7U.js} +1 -1
- package/dist/bundle/mail.js +12 -11
- package/dist/bundle/{mcp-L7SRVDRN.js → mcp-GAPDLFWA.js} +2 -2
- package/dist/bundle/{node-OXT7WE2T.js → node-NYBBFHGN.js} +2 -2
- package/dist/bundle/{node-sqlite-X2PSEHA4.js → node-sqlite-CWCNWO75.js} +5 -5
- package/dist/bundle/{oauth-MYK4BSYN.js → oauth-DVX45EME.js} +7 -7
- package/dist/bundle/{open-GONIP2DH.js → open-M7EQWWHL.js} +4 -4
- package/dist/bundle/{openai-YJFNUOQT.js → openai-63KRU5H2.js} +14 -14
- package/dist/bundle/{process-7IEUUSRK.js → process-DV5GQIGM.js} +1 -1
- package/dist/bundle/{program-Q73TTG3E.js → program-EPR3QCMX.js} +20562 -7617
- package/dist/bundle/{prompt-SKQ72KN7.js → prompt-EPWPWTB3.js} +5 -5
- package/dist/bundle/{recognize-GC7CU2DB.js → recognize-R46KDXK4.js} +4 -4
- package/dist/bundle/{remote-UEXFZP2I.js → remote-RENCYB5X.js} +7 -7
- package/dist/bundle/{rerank-2U2NDEZP.js → rerank-VHPC5O73.js} +7 -7
- package/dist/bundle/{serve-OOGKFEWL.js → serve-KCH3ORYO.js} +9 -9
- package/dist/bundle/server-FMCPE2ZK.js +63 -0
- package/dist/bundle/{server-FZ3LFQTF.js → server-HZVXSK7B.js} +38 -38
- package/dist/bundle/{sqlite-runtime-GIQSW6LJ.js → sqlite-runtime-XP7ZJCKJ.js} +5 -5
- package/dist/bundle/{state-EMFXPQVZ.js → state-GQ4MEQJ5.js} +6 -6
- package/dist/bundle/{store-LDI62AUG.js → store-CQNLNINW.js} +13 -13
- package/dist/bundle/{store-BDU4UPNK.js → store-WT7YTR3Y.js} +15 -15
- package/dist/bundle/store-maintenance-command-YYR42IT4.js +52 -0
- package/dist/commands/attachments.d.ts +3 -0
- package/dist/commands/attachments.d.ts.map +1 -0
- package/dist/commands/attachments.js +97 -0
- package/dist/commands/attachments.js.map +1 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +11 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/drafts-send.d.ts +7 -0
- package/dist/commands/drafts-send.d.ts.map +1 -0
- package/dist/commands/drafts-send.js +58 -0
- package/dist/commands/drafts-send.js.map +1 -0
- package/dist/commands/drafts.d.ts +3 -0
- package/dist/commands/drafts.d.ts.map +1 -0
- package/dist/commands/drafts.js +239 -0
- package/dist/commands/drafts.js.map +1 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +275 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/folders.d.ts.map +1 -1
- package/dist/commands/folders.js +120 -2
- package/dist/commands/folders.js.map +1 -1
- package/dist/commands/messages.d.ts +25 -0
- package/dist/commands/messages.d.ts.map +1 -0
- package/dist/commands/messages.js +502 -0
- package/dist/commands/messages.js.map +1 -0
- package/dist/commands/read.d.ts +4 -1
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +12 -2
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/rules.d.ts +3 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +292 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/send.d.ts +39 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +309 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/server-search.d.ts +8 -0
- package/dist/commands/server-search.d.ts.map +1 -0
- package/dist/commands/server-search.js +38 -0
- package/dist/commands/server-search.js.map +1 -0
- package/dist/commands/stats.d.ts +3 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +90 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/sync.d.ts +7 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +22 -7
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/tags.d.ts +3 -0
- package/dist/commands/tags.d.ts.map +1 -0
- package/dist/commands/tags.js +144 -0
- package/dist/commands/tags.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +47 -1
- package/dist/config.js.map +1 -1
- package/dist/export/format.d.ts +19 -0
- package/dist/export/format.d.ts.map +1 -0
- package/dist/export/format.js +40 -0
- package/dist/export/format.js.map +1 -0
- package/dist/export/journal.d.ts +32 -0
- package/dist/export/journal.d.ts.map +1 -0
- package/dist/export/journal.js +84 -0
- package/dist/export/journal.js.map +1 -0
- package/dist/export/write.d.ts +14 -0
- package/dist/export/write.d.ts.map +1 -0
- package/dist/export/write.js +42 -0
- package/dist/export/write.js.map +1 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +3 -0
- package/dist/identity.js.map +1 -1
- package/dist/mail/backend.d.ts +81 -1
- package/dist/mail/backend.d.ts.map +1 -1
- package/dist/mail/changes.d.ts +91 -0
- package/dist/mail/changes.d.ts.map +1 -0
- package/dist/mail/changes.js +198 -0
- package/dist/mail/changes.js.map +1 -0
- package/dist/mail/cleanup.d.ts +41 -0
- package/dist/mail/cleanup.d.ts.map +1 -0
- package/dist/mail/cleanup.js +119 -0
- package/dist/mail/cleanup.js.map +1 -0
- package/dist/mail/compose.d.ts +73 -0
- package/dist/mail/compose.d.ts.map +1 -0
- package/dist/mail/compose.js +122 -0
- package/dist/mail/compose.js.map +1 -0
- package/dist/mail/doctor.d.ts +25 -0
- package/dist/mail/doctor.d.ts.map +1 -0
- package/dist/mail/doctor.js +39 -0
- package/dist/mail/doctor.js.map +1 -0
- package/dist/mail/drafts.d.ts +54 -0
- package/dist/mail/drafts.d.ts.map +1 -0
- package/dist/mail/drafts.js +104 -0
- package/dist/mail/drafts.js.map +1 -0
- package/dist/mail/imap/connection.d.ts +7 -0
- package/dist/mail/imap/connection.d.ts.map +1 -1
- package/dist/mail/imap/connection.js +24 -2
- package/dist/mail/imap/connection.js.map +1 -1
- package/dist/mail/imap/index.d.ts +1 -1
- package/dist/mail/imap/index.d.ts.map +1 -1
- package/dist/mail/imap/index.js +12 -2
- package/dist/mail/imap/index.js.map +1 -1
- package/dist/mail/imap/messages.d.ts +5 -6
- package/dist/mail/imap/messages.d.ts.map +1 -1
- package/dist/mail/imap/messages.js +14 -28
- package/dist/mail/imap/messages.js.map +1 -1
- package/dist/mail/imap/read.d.ts.map +1 -1
- package/dist/mail/imap/read.js +27 -13
- package/dist/mail/imap/read.js.map +1 -1
- package/dist/mail/imap/search.d.ts +14 -0
- package/dist/mail/imap/search.d.ts.map +1 -0
- package/dist/mail/imap/search.js +49 -0
- package/dist/mail/imap/search.js.map +1 -0
- package/dist/mail/imap/write.d.ts +5 -0
- package/dist/mail/imap/write.d.ts.map +1 -0
- package/dist/mail/imap/write.js +93 -0
- package/dist/mail/imap/write.js.map +1 -0
- package/dist/mail/organise.d.ts +116 -0
- package/dist/mail/organise.d.ts.map +1 -0
- package/dist/mail/organise.js +531 -0
- package/dist/mail/organise.js.map +1 -0
- package/dist/mail/rethread.d.ts +1 -2
- package/dist/mail/rethread.d.ts.map +1 -1
- package/dist/mail/rethread.js +8 -3
- package/dist/mail/rethread.js.map +1 -1
- package/dist/mail/rules/apply.d.ts +40 -0
- package/dist/mail/rules/apply.d.ts.map +1 -0
- package/dist/mail/rules/apply.js +114 -0
- package/dist/mail/rules/apply.js.map +1 -0
- package/dist/mail/rules/file.d.ts +9 -0
- package/dist/mail/rules/file.d.ts.map +1 -0
- package/dist/mail/rules/file.js +51 -0
- package/dist/mail/rules/file.js.map +1 -0
- package/dist/mail/rules/filters.d.ts +11 -0
- package/dist/mail/rules/filters.d.ts.map +1 -0
- package/dist/mail/rules/filters.js +150 -0
- package/dist/mail/rules/filters.js.map +1 -0
- package/dist/mail/rules/folders.d.ts +11 -0
- package/dist/mail/rules/folders.d.ts.map +1 -0
- package/dist/mail/rules/folders.js +17 -0
- package/dist/mail/rules/folders.js.map +1 -0
- package/dist/mail/rules/query.d.ts +15 -0
- package/dist/mail/rules/query.d.ts.map +1 -0
- package/dist/mail/rules/query.js +103 -0
- package/dist/mail/rules/query.js.map +1 -0
- package/dist/mail/rules/store.d.ts +9 -0
- package/dist/mail/rules/store.d.ts.map +1 -0
- package/dist/mail/rules/store.js +17 -0
- package/dist/mail/rules/store.js.map +1 -0
- package/dist/mail/save.d.ts +12 -0
- package/dist/mail/save.d.ts.map +1 -0
- package/dist/mail/save.js +71 -0
- package/dist/mail/save.js.map +1 -0
- package/dist/mail/send.d.ts +67 -0
- package/dist/mail/send.d.ts.map +1 -0
- package/dist/mail/send.js +172 -0
- package/dist/mail/send.js.map +1 -0
- package/dist/mail/smtp.d.ts +4 -0
- package/dist/mail/smtp.d.ts.map +1 -0
- package/dist/mail/smtp.js +97 -0
- package/dist/mail/smtp.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +26 -4
- package/dist/program.js.map +1 -1
- package/dist/providers.d.ts +71 -6
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +38 -9
- package/dist/providers.js.map +1 -1
- package/dist/reasons.d.ts +9 -0
- package/dist/reasons.d.ts.map +1 -0
- package/dist/reasons.js +21 -0
- package/dist/reasons.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -5
- package/skills/mail-cli/SKILL.md +30 -3
- package/dist/bundle/chunk-YD5WV757.js +0 -27
- package/dist/bundle/command-contract-GPUED3GT.js +0 -34
- package/dist/bundle/first-screen-M7MT776O.js +0 -32
- package/dist/bundle/server-4JMHDHZ7.js +0 -63
- package/dist/bundle/store-maintenance-command-OFRPORN2.js +0 -52
- /package/dist/bundle/{chunk-NPGVOZVK.js → chunk-WJWLHLQ7.js} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { MailSource } from "@wirecat/cli-core/mail";
|
|
2
|
+
import { type MailAccount, type MailRule, type TagOutcome } from "@wirecat/cli-core/mail";
|
|
3
|
+
import type { MessageStore } from "@wirecat/cli-core/store";
|
|
4
|
+
export interface RuleEffect {
|
|
5
|
+
rule: MailRule;
|
|
6
|
+
emails: number;
|
|
7
|
+
/** Text this rule's emails hold in the store: bodies and attachment text. */
|
|
8
|
+
bytes: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ApplyResult {
|
|
11
|
+
account: string;
|
|
12
|
+
applied: boolean;
|
|
13
|
+
listed: number;
|
|
14
|
+
/** Stored in full, matched by an envelope rule: cut to envelopes, or would be. */
|
|
15
|
+
envelopes: number;
|
|
16
|
+
bytes: number;
|
|
17
|
+
/** Stored in full, matched only by a skip rule: left as stored, since removing them is another decision. */
|
|
18
|
+
skipMatched: number;
|
|
19
|
+
/** Stored in full, matched by a protected rule: never cut, whatever else matches. */
|
|
20
|
+
protectedKept: number;
|
|
21
|
+
/** Stored emails a rule's tag was put on, or would be. */
|
|
22
|
+
tagged: number;
|
|
23
|
+
rules: RuleEffect[];
|
|
24
|
+
tagsReused?: TagOutcome["reused"];
|
|
25
|
+
tagsSkipped?: TagOutcome["skipped"];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Cuts mail stored in full that an envelope rule now matches down to what an envelope sync stores. Matching
|
|
29
|
+
* reads the account's headers from the server, as sync does: List-Id is not stored with a full email.
|
|
30
|
+
*/
|
|
31
|
+
export declare const applyRules: ({ store, source, account, folders, since, apply, now, }: {
|
|
32
|
+
store: MessageStore;
|
|
33
|
+
source: Pick<MailSource, "list">;
|
|
34
|
+
account: Pick<MailAccount, "address" | "rules">;
|
|
35
|
+
folders: readonly string[];
|
|
36
|
+
since: Date;
|
|
37
|
+
apply: boolean;
|
|
38
|
+
now?: () => number;
|
|
39
|
+
}) => Promise<ApplyResult>;
|
|
40
|
+
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,QAAQ,EAKb,KAAK,UAAU,EAEhB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAEvE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,4GAA4G;IAC5G,WAAW,EAAE,MAAM,CAAA;IACnB,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAA;IACrB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;CACpC;AAID;;;GAGG;AACH,eAAO,MAAM,UAAU,4DAQpB;IACD,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IAChC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,OAAO,CAAC,CAAA;IAC/C,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,KAAK,EAAE,IAAI,CAAA;IACX,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB,KAAG,OAAO,CAAC,WAAW,CAmGtB,CAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { decide, ENVELOPE, envelopeBox, factsOf, ruleTagger, sameMatch, settleRuleTags, tagsOf, } from "@wirecat/cli-core/mail";
|
|
2
|
+
const textBytes = (text) => (text ? Buffer.byteLength(text) : 0);
|
|
3
|
+
/**
|
|
4
|
+
* Cuts mail stored in full that an envelope rule now matches down to what an envelope sync stores. Matching
|
|
5
|
+
* reads the account's headers from the server, as sync does: List-Id is not stored with a full email.
|
|
6
|
+
*/
|
|
7
|
+
export const applyRules = async ({ store, source, account, folders, since, apply, now = Date.now, }) => {
|
|
8
|
+
const startedAt = now();
|
|
9
|
+
const key = { provider: "email", account: account.address.toLowerCase() };
|
|
10
|
+
const rules = account.rules ?? [];
|
|
11
|
+
const effects = rules
|
|
12
|
+
.filter(({ action, protected: kept }) => action === ENVELOPE && !kept)
|
|
13
|
+
.map((rule) => ({ rule, emails: 0, bytes: 0 }));
|
|
14
|
+
const result = {
|
|
15
|
+
account: key.account,
|
|
16
|
+
applied: apply,
|
|
17
|
+
listed: 0,
|
|
18
|
+
envelopes: 0,
|
|
19
|
+
bytes: 0,
|
|
20
|
+
skipMatched: 0,
|
|
21
|
+
protectedKept: 0,
|
|
22
|
+
tagged: 0,
|
|
23
|
+
rules: effects,
|
|
24
|
+
};
|
|
25
|
+
if (effects.length === 0 && !rules.some(({ tags, protected: kept }) => tags?.length || kept))
|
|
26
|
+
return result;
|
|
27
|
+
const stored = await store.accounts.storedAccount(key).catch(() => null);
|
|
28
|
+
if (stored === null)
|
|
29
|
+
return result;
|
|
30
|
+
const listed = new Map();
|
|
31
|
+
for (const folder of folders)
|
|
32
|
+
for (const item of await source.list(since, folder, { headers: true }))
|
|
33
|
+
listed.set(item.messageId, item);
|
|
34
|
+
result.listed = listed.size;
|
|
35
|
+
const full = new Set((await store.mail.emailRefs({ accountId: stored.id }))
|
|
36
|
+
.filter(({ deleted, mailboxes }) => !deleted && !mailboxes.some(({ kind }) => kind === ENVELOPE))
|
|
37
|
+
.map(({ externalId }) => externalId));
|
|
38
|
+
const effectOf = (match) => effects.find(({ rule }) => sameMatch(rule.match, match));
|
|
39
|
+
const all = new Set((await store.mail.emailRefs({ accountId: stored.id })).map(({ externalId }) => externalId));
|
|
40
|
+
const factsFor = factsOf([...listed.values()], rules);
|
|
41
|
+
const tagger = ruleTagger(store, key);
|
|
42
|
+
const chosen = [];
|
|
43
|
+
for (const item of listed.values()) {
|
|
44
|
+
const facts = factsFor(item);
|
|
45
|
+
const tags = all.has(item.messageId) ? tagsOf(rules, facts) : [];
|
|
46
|
+
if (tags.length) {
|
|
47
|
+
result.tagged++;
|
|
48
|
+
if (apply)
|
|
49
|
+
await tagger.tag({ type: "message", chatId: item.threadId, messageId: item.messageId }, tags);
|
|
50
|
+
}
|
|
51
|
+
if (!full.has(item.messageId))
|
|
52
|
+
continue;
|
|
53
|
+
const rule = decide(rules, facts);
|
|
54
|
+
if (rule?.protected)
|
|
55
|
+
result.protectedKept++;
|
|
56
|
+
if (rule?.action === "skip")
|
|
57
|
+
result.skipMatched++;
|
|
58
|
+
if (rule?.action === ENVELOPE)
|
|
59
|
+
chosen.push({ item, rule });
|
|
60
|
+
}
|
|
61
|
+
for (const { item, rule } of chosen) {
|
|
62
|
+
const email = await store.mail.email(stored.id, item.messageId);
|
|
63
|
+
if (email === null)
|
|
64
|
+
continue;
|
|
65
|
+
const bytes = textBytes(email.bodyText) +
|
|
66
|
+
textBytes(email.bodyHtml) +
|
|
67
|
+
email.attachments.reduce((sum, { text }) => sum + textBytes(text), 0);
|
|
68
|
+
const effect = effectOf(rule.match);
|
|
69
|
+
effect.emails++;
|
|
70
|
+
effect.bytes += bytes;
|
|
71
|
+
result.envelopes++;
|
|
72
|
+
result.bytes += bytes;
|
|
73
|
+
if (!apply)
|
|
74
|
+
continue;
|
|
75
|
+
const [placed] = await store.mail.inThreads(stored.id, [item.messageId]);
|
|
76
|
+
const subject = email.subject ?? item.headers?.subject;
|
|
77
|
+
const listId = item.headers?.listId;
|
|
78
|
+
await store.mail.saveThread({
|
|
79
|
+
accountId: stored.id,
|
|
80
|
+
externalId: placed?.threadExternalId ?? item.threadId,
|
|
81
|
+
now: startedAt,
|
|
82
|
+
emails: [
|
|
83
|
+
{
|
|
84
|
+
externalId: item.messageId,
|
|
85
|
+
subject: null,
|
|
86
|
+
from: email.fromAddress === null ? null : { address: email.fromAddress, name: email.fromName },
|
|
87
|
+
sentAt: email.sentAt,
|
|
88
|
+
bodyText: null,
|
|
89
|
+
bodyHtml: null,
|
|
90
|
+
snippet: null,
|
|
91
|
+
size: email.size ?? item.size ?? null,
|
|
92
|
+
mailboxes: [envelopeBox(rule.match)],
|
|
93
|
+
mailRuleId: rule.id ?? null,
|
|
94
|
+
attachments: [],
|
|
95
|
+
metadata: {
|
|
96
|
+
...email.metadata,
|
|
97
|
+
rule: { action: rule.action, match: { ...rule.match } },
|
|
98
|
+
...(subject ? { subject } : {}),
|
|
99
|
+
...(listId ? { listId } : {}),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const tagging = tagger.outcome();
|
|
106
|
+
if (apply)
|
|
107
|
+
await settleRuleTags(store, stored.id, rules, tagging);
|
|
108
|
+
if (tagging.reused.length)
|
|
109
|
+
result.tagsReused = tagging.reused;
|
|
110
|
+
if (tagging.skipped.length)
|
|
111
|
+
result.tagsSkipped = tagging.skipped;
|
|
112
|
+
return result;
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/mail/rules/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type MailRule } from "@wirecat/cli-core/mail";
|
|
2
|
+
/** What `rules export` prints and `rules import` reads back; memo writes and reads the same file. */
|
|
3
|
+
export interface RulesFile {
|
|
4
|
+
version: 1;
|
|
5
|
+
account: string;
|
|
6
|
+
rules: Omit<MailRule, "id">[];
|
|
7
|
+
}
|
|
8
|
+
export declare const rulesFileOf: (text: string, path: string, exporter: string) => MailRule[];
|
|
9
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/file.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,QAAQ,EAAsB,MAAM,wBAAwB,CAAA;AAE1G,qGAAqG;AACrG,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,CAAC,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAA;CAC9B;AAqBD,eAAO,MAAM,WAAW,SAAU,MAAM,QAAQ,MAAM,YAAY,MAAM,KAAG,QAAQ,EA4BlF,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { conditionKeys } from "@wirecat/cli-core/mail";
|
|
3
|
+
const ACTIONS = ["skip", "envelope", "keep"];
|
|
4
|
+
const CONDITIONS = [
|
|
5
|
+
"from",
|
|
6
|
+
"from_contains",
|
|
7
|
+
"domain",
|
|
8
|
+
"to",
|
|
9
|
+
"list_id",
|
|
10
|
+
"subject",
|
|
11
|
+
"older_than_days",
|
|
12
|
+
"larger_than_bytes",
|
|
13
|
+
"has_attachments",
|
|
14
|
+
"label",
|
|
15
|
+
"unread",
|
|
16
|
+
"bulk",
|
|
17
|
+
"score_at_least",
|
|
18
|
+
"preset",
|
|
19
|
+
];
|
|
20
|
+
export const rulesFileOf = (text, path, exporter) => {
|
|
21
|
+
const bad = (why) => new CliError("validation_error", `${path}: ${why}`, { reason: "mail.rules-file-invalid" });
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(text);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw bad("not JSON");
|
|
28
|
+
}
|
|
29
|
+
const rules = parsed?.rules;
|
|
30
|
+
if (!Array.isArray(rules))
|
|
31
|
+
throw bad(`no rules list; write it with ${exporter}`);
|
|
32
|
+
return rules.map((raw, index) => {
|
|
33
|
+
const rule = (raw ?? {});
|
|
34
|
+
if (!ACTIONS.includes(rule.action) || conditionKeys(rule.match ?? {}).length === 0)
|
|
35
|
+
throw bad(`rule ${index + 1} needs at least one match condition and an action`);
|
|
36
|
+
const match = rule.match;
|
|
37
|
+
const unknown = conditionKeys(match).filter((key) => !CONDITIONS.includes(key));
|
|
38
|
+
if (unknown.length)
|
|
39
|
+
throw bad(`rule ${index + 1} has conditions mail does not know: ${unknown.join(", ")}`);
|
|
40
|
+
return {
|
|
41
|
+
match,
|
|
42
|
+
action: rule.action,
|
|
43
|
+
...(Array.isArray(rule.tags) ? { tags: rule.tags.filter((tag) => typeof tag === "string") } : {}),
|
|
44
|
+
...(typeof rule.reason === "string" ? { reason: rule.reason } : {}),
|
|
45
|
+
...(rule.protected === true ? { protected: true } : {}),
|
|
46
|
+
...(typeof rule.source === "string" ? { source: rule.source } : {}),
|
|
47
|
+
decided_at: typeof rule.decided_at === "string" ? rule.decided_at : new Date().toISOString(),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/mail/rules/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MailRule } from "@wirecat/cli-core/mail";
|
|
2
|
+
export interface FiltersProposal {
|
|
3
|
+
rules: MailRule[];
|
|
4
|
+
skipped: {
|
|
5
|
+
filter: string;
|
|
6
|
+
why: string;
|
|
7
|
+
}[];
|
|
8
|
+
}
|
|
9
|
+
/** The rules a Takeout Filters.json suggests; nothing is saved here. */
|
|
10
|
+
export declare const importFilters: (json: string, path: string, now?: Date) => FiltersProposal;
|
|
11
|
+
//# sourceMappingURL=filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/filters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,wBAAwB,CAAA;AAGrE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC3C;AA2ID,wEAAwE;AACxE,eAAO,MAAM,aAAa,SAAU,MAAM,QAAQ,MAAM,iBAAqB,eAkB5E,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { isAddress, senderMatch } from "./query.js";
|
|
3
|
+
const text = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
|
|
4
|
+
const list = (value) => Array.isArray(value) ? value.flatMap((item) => (typeof item === "string" ? [item] : [])) : [];
|
|
5
|
+
const yes = (value) => value === true || value === "true";
|
|
6
|
+
const SYSTEM = new Set(["INBOX", "UNREAD", "SPAM", "TRASH", "IMPORTANT", "STARRED", "CATEGORY_PROMOTIONS"]);
|
|
7
|
+
/** `a OR b`, `(a OR b)`, `{a b}`: Gmail's ways of writing either of several values. */
|
|
8
|
+
const alternatives = (value) => value
|
|
9
|
+
.replace(/^[({]|[)}]$/g, "")
|
|
10
|
+
.split(/\s+OR\s+|\s+|,/)
|
|
11
|
+
.map((part) => part.trim())
|
|
12
|
+
.filter((part) => part !== "" && part !== "OR");
|
|
13
|
+
/**
|
|
14
|
+
* A filter's search query, as the Takeout export writes it (`from:a from:b to:me@x.example word`). A sender cannot
|
|
15
|
+
* be two addresses at once, so repeated from: or to: terms are read as either one: one rule per term.
|
|
16
|
+
*/
|
|
17
|
+
const queryOf = (query) => {
|
|
18
|
+
const parsed = { from: [], to: [], words: [], attachment: false, unknown: [] };
|
|
19
|
+
for (const [, field, grouped, quoted, plain, word] of query.matchAll(/(\w+):(?:([({][^)}]*[)}])|"([^"]*)"|(\S+))|(\S+)/g)) {
|
|
20
|
+
if (word !== undefined) {
|
|
21
|
+
if (word !== "OR")
|
|
22
|
+
parsed.words.push(word.replace(/^"|"$/g, ""));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const value = grouped ?? quoted ?? plain ?? "";
|
|
26
|
+
switch (field?.toLowerCase()) {
|
|
27
|
+
case "from":
|
|
28
|
+
for (const sender of alternatives(value))
|
|
29
|
+
parsed.from.push(sender);
|
|
30
|
+
break;
|
|
31
|
+
case "to":
|
|
32
|
+
case "cc":
|
|
33
|
+
for (const recipient of alternatives(value))
|
|
34
|
+
parsed.to.push(recipient);
|
|
35
|
+
break;
|
|
36
|
+
case "subject":
|
|
37
|
+
for (const part of value.replace(/^[({]|[)}]$/g, "").split(/\s+/))
|
|
38
|
+
if (part)
|
|
39
|
+
parsed.words.push(part);
|
|
40
|
+
break;
|
|
41
|
+
case "has":
|
|
42
|
+
if (/^attachments?$/i.test(value))
|
|
43
|
+
parsed.attachment = true;
|
|
44
|
+
else
|
|
45
|
+
parsed.unknown.push(`has:${value}`);
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
parsed.unknown.push(`${field}:`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
};
|
|
53
|
+
/** The matches one filter's conditions make: one per sender or recipient it names, each with the shared rest. */
|
|
54
|
+
const matchesOf = (criteria) => {
|
|
55
|
+
const query = queryOf([
|
|
56
|
+
text(criteria.query),
|
|
57
|
+
text(criteria.hasTheWord),
|
|
58
|
+
text(criteria.from) && `from:(${text(criteria.from)})`,
|
|
59
|
+
text(criteria.to) && `to:(${text(criteria.to)})`,
|
|
60
|
+
]
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join(" "));
|
|
63
|
+
if (text(criteria.negatedQuery) ?? text(criteria.doesNotHaveTheWord))
|
|
64
|
+
return "it excludes words, which mail rules cannot do";
|
|
65
|
+
if (criteria.size !== undefined)
|
|
66
|
+
return "it matches size, which this import does not map";
|
|
67
|
+
if (query.unknown.length)
|
|
68
|
+
return `it uses ${query.unknown.join(", ")}, which this import does not map`;
|
|
69
|
+
const badTo = query.to.find((value) => !isAddress(value));
|
|
70
|
+
if (badTo !== undefined)
|
|
71
|
+
return "it names a recipient that is not one address";
|
|
72
|
+
const subject = [...query.words, ...(text(criteria.subject) ? [text(criteria.subject)] : [])].join(" ");
|
|
73
|
+
const shared = {
|
|
74
|
+
...(subject === "" ? {} : { subject }),
|
|
75
|
+
...(query.attachment || yes(criteria.hasAttachment) ? { has_attachments: true } : {}),
|
|
76
|
+
};
|
|
77
|
+
const senders = query.from.length ? query.from.map(senderMatch) : [{}];
|
|
78
|
+
const recipients = query.to.length ? query.to.map((to) => ({ to: to.toLowerCase() })) : [{}];
|
|
79
|
+
const matches = senders.flatMap((sender) => recipients.map((recipient) => ({ ...sender, ...recipient, ...shared })));
|
|
80
|
+
return Object.keys(matches[0] ?? {}).length === 0 ? "it has no condition a mail rule can match" : matches;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* One filter, in any shape Google writes them: the Gmail API's `{criteria, action}` (developers.google.com,
|
|
84
|
+
* users.settings.filters), the settings export's flat properties (`from`, `hasTheWord`, `shouldArchive`, `label`),
|
|
85
|
+
* or Takeout's `{query, labelsToAdd, labelsToRemove}`. Words are matched in the subject only; Gmail searched the
|
|
86
|
+
* whole email, so such a rule says so in its reason.
|
|
87
|
+
*/
|
|
88
|
+
const rulesOf = (raw, decidedAt) => {
|
|
89
|
+
const criteria = raw.criteria ?? raw;
|
|
90
|
+
const action = raw.action ?? raw;
|
|
91
|
+
const matches = matchesOf(criteria);
|
|
92
|
+
if (typeof matches === "string")
|
|
93
|
+
return matches;
|
|
94
|
+
// The API writes system labels as INBOX, Takeout as Inbox.
|
|
95
|
+
const removed = [...list(action.removeLabelIds), ...list(action.labelsToRemove)].map((id) => id.toUpperCase());
|
|
96
|
+
const addedAsWritten = [...list(action.addLabelIds), ...list(action.labelsToAdd)];
|
|
97
|
+
const added = addedAsWritten.map((id) => id.toUpperCase());
|
|
98
|
+
const hides = removed.includes("INBOX") ||
|
|
99
|
+
removed.includes("UNREAD") ||
|
|
100
|
+
added.includes("TRASH") ||
|
|
101
|
+
yes(action.shouldArchive) ||
|
|
102
|
+
yes(action.shouldMarkAsRead) ||
|
|
103
|
+
yes(action.shouldTrash);
|
|
104
|
+
const tags = [
|
|
105
|
+
...addedAsWritten.filter((id) => !SYSTEM.has(id.toUpperCase())),
|
|
106
|
+
...(text(action.label) === undefined ? [] : [text(action.label)]),
|
|
107
|
+
];
|
|
108
|
+
if (!hides && tags.length === 0)
|
|
109
|
+
return "it does nothing a mail rule can mirror";
|
|
110
|
+
return matches.map((match) => ({
|
|
111
|
+
match,
|
|
112
|
+
action: hides ? "envelope" : "keep",
|
|
113
|
+
...(tags.length ? { tags } : {}),
|
|
114
|
+
source: "gmail-filter",
|
|
115
|
+
reason: match.subject
|
|
116
|
+
? "from a Gmail filter; Gmail searched these words anywhere, the rule matches the subject"
|
|
117
|
+
: "from a Gmail filter",
|
|
118
|
+
decided_at: decidedAt,
|
|
119
|
+
}));
|
|
120
|
+
};
|
|
121
|
+
const nameOf = (raw, index) => {
|
|
122
|
+
const criteria = raw.criteria ?? raw;
|
|
123
|
+
return text(criteria.query) ?? text(criteria.from) ?? text(criteria.subject) ?? text(raw.id) ?? `filter ${index + 1}`;
|
|
124
|
+
};
|
|
125
|
+
/** The rules a Takeout Filters.json suggests; nothing is saved here. */
|
|
126
|
+
export const importFilters = (json, path, now = new Date()) => {
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(json);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw new CliError("validation_error", `${path}: not JSON`, { reason: "mail.filters-file-invalid" });
|
|
133
|
+
}
|
|
134
|
+
const filters = Array.isArray(parsed) ? parsed : (parsed?.filters ?? parsed?.filter);
|
|
135
|
+
if (!Array.isArray(filters))
|
|
136
|
+
throw new CliError("validation_error", `${path}: no list of filters`, { reason: "mail.filters-file-invalid" });
|
|
137
|
+
const proposal = { rules: [], skipped: [] };
|
|
138
|
+
for (const [index, raw] of filters.entries()) {
|
|
139
|
+
if (raw === null || typeof raw !== "object")
|
|
140
|
+
continue;
|
|
141
|
+
const outcome = rulesOf(raw, now.toISOString());
|
|
142
|
+
if (typeof outcome === "string")
|
|
143
|
+
proposal.skipped.push({ filter: nameOf(raw, index), why: outcome });
|
|
144
|
+
else
|
|
145
|
+
for (const rule of outcome)
|
|
146
|
+
proposal.rules.push(rule);
|
|
147
|
+
}
|
|
148
|
+
return proposal;
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=filters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.js","sourceRoot":"","sources":["../../../src/mail/rules/filters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MailBackend } from "../backend.js";
|
|
2
|
+
/**
|
|
3
|
+
* The folders `sync` reads: `--folder`, else the profile's, else the provider's by role (INBOX and Sent, All Mail on
|
|
4
|
+
* Gmail). `rules apply` lists the same ones, so a rule judges the mail sync stored from them.
|
|
5
|
+
*/
|
|
6
|
+
export declare const syncFoldersOf: (mailbox: {
|
|
7
|
+
address?: string;
|
|
8
|
+
host?: string;
|
|
9
|
+
folders?: string[];
|
|
10
|
+
}, backend: Pick<MailBackend, "folderList">, explicit?: string[]) => Promise<string[]>;
|
|
11
|
+
//# sourceMappingURL=folders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/folders.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,aAAa,YACf;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,WACvD,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,aAC7B,MAAM,EAAE,KAClB,OAAO,CAAC,MAAM,EAAE,CAQlB,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DEFAULT_SYNC, providerFor } from "../../providers.js";
|
|
2
|
+
/**
|
|
3
|
+
* The folders `sync` reads: `--folder`, else the profile's, else the provider's by role (INBOX and Sent, All Mail on
|
|
4
|
+
* Gmail). `rules apply` lists the same ones, so a rule judges the mail sync stored from them.
|
|
5
|
+
*/
|
|
6
|
+
export const syncFoldersOf = async (mailbox, backend, explicit) => {
|
|
7
|
+
if (explicit?.length)
|
|
8
|
+
return explicit;
|
|
9
|
+
if (mailbox.folders?.length)
|
|
10
|
+
return mailbox.folders;
|
|
11
|
+
const defaults = (mailbox.address ? providerFor(mailbox.address, mailbox.host)?.sync : undefined) ?? DEFAULT_SYNC;
|
|
12
|
+
const chosen = (await backend.folderList())
|
|
13
|
+
.filter((folder) => folder.role && defaults.roles.includes(folder.role))
|
|
14
|
+
.map(({ path }) => path);
|
|
15
|
+
return chosen.length ? chosen : [defaults.otherwise];
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=folders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders.js","sourceRoot":"","sources":["../../../src/mail/rules/folders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAG9D;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,OAAgE,EAChE,OAAwC,EACxC,QAAmB,EACA,EAAE;IACrB,IAAI,QAAQ,EAAE,MAAM;QAAE,OAAO,QAAQ,CAAA;IACrC,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,CAAA;IACnD,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,YAAY,CAAA;IACjH,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;SACxC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SACvE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAC1B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;AACtD,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MailAction, MailRuleMatch } from "@wirecat/cli-core/mail";
|
|
2
|
+
export declare const FILTER_HELP: string;
|
|
3
|
+
/** `from:` as Gmail reads it: one address, a whole domain (`shop.example`, `@shop.example`), or a word it contains. */
|
|
4
|
+
export declare const senderMatch: (value: string) => MailRuleMatch;
|
|
5
|
+
export declare const isAddress: (value: string) => boolean;
|
|
6
|
+
/** The conditions of a rule's --filter query. */
|
|
7
|
+
export declare const filterMatch: (query: string) => MailRuleMatch;
|
|
8
|
+
export declare const ACTION_HELP: string;
|
|
9
|
+
/** `envelope,tag:promo` → the store action, protection and tags. */
|
|
10
|
+
export declare const actionsOf: (value: string) => {
|
|
11
|
+
action: MailAction;
|
|
12
|
+
protect: boolean;
|
|
13
|
+
tags: string[];
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAEvE,eAAO,MAAM,WAAW,QAEwD,CAAA;AAMhF,uHAAuH;AACvH,eAAO,MAAM,WAAW,UAAW,MAAM,KAAG,aAI3C,CAAA;AAED,eAAO,MAAM,SAAS,UAAW,MAAM,YAAwB,CAAA;AAS/D,iDAAiD;AACjD,eAAO,MAAM,WAAW,UAAW,MAAM,KAAG,aAmD3C,CAAA;AAED,eAAO,MAAM,WAAW,QAGoF,CAAA;AAI5G,oEAAoE;AACpE,eAAO,MAAM,SAAS,UAAW,MAAM,KAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAY/F,CAAA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
export const FILTER_HELP = "from:<address, domain or word>, to:<address>, subject:<word> (or a bare word), older:<n>d, larger:<size> (5MB, 300KB), has:attachment, label:<name> " +
|
|
3
|
+
'(label:"Two words"), is:unread, is:bulk, score:<n> (at least); all must hold';
|
|
4
|
+
const bad = (why) => new CliError("validation_error", `--filter: ${why}`);
|
|
5
|
+
const ADDRESS = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
+
/** `from:` as Gmail reads it: one address, a whole domain (`shop.example`, `@shop.example`), or a word it contains. */
|
|
7
|
+
export const senderMatch = (value) => {
|
|
8
|
+
if (ADDRESS.test(value))
|
|
9
|
+
return { from: value.toLowerCase() };
|
|
10
|
+
const domain = value.replace(/^@/, "");
|
|
11
|
+
return /^[a-z0-9-]+(\.[a-z0-9-]+)+$/i.test(domain) ? { domain: domain.toLowerCase() } : { from_contains: value };
|
|
12
|
+
};
|
|
13
|
+
export const isAddress = (value) => ADDRESS.test(value);
|
|
14
|
+
const bytesOf = (value) => {
|
|
15
|
+
const match = /^(\d+(?:\.\d+)?)(b|kb|mb|gb)?$/i.exec(value);
|
|
16
|
+
if (match === null)
|
|
17
|
+
throw bad(`larger:${value} is not a size like 5MB, 300KB or 2048`);
|
|
18
|
+
const unit = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3 }[(match[2] ?? "b").toLowerCase()];
|
|
19
|
+
return Math.round(Number(match[1]) * unit);
|
|
20
|
+
};
|
|
21
|
+
/** The conditions of a rule's --filter query. */
|
|
22
|
+
export const filterMatch = (query) => {
|
|
23
|
+
const words = [];
|
|
24
|
+
const match = {};
|
|
25
|
+
for (const [, field, quoted, plain, word] of query.matchAll(/(\w+):(?:"([^"]*)"|(\S+))|(\S+)/g)) {
|
|
26
|
+
const value = quoted ?? plain ?? "";
|
|
27
|
+
if (word !== undefined) {
|
|
28
|
+
words.push(word);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
switch (field?.toLowerCase()) {
|
|
32
|
+
case "from":
|
|
33
|
+
Object.assign(match, senderMatch(value));
|
|
34
|
+
break;
|
|
35
|
+
case "to":
|
|
36
|
+
if (!ADDRESS.test(value))
|
|
37
|
+
throw bad(`to:${value} needs one address, like to:me@example.com`);
|
|
38
|
+
match.to = value.toLowerCase();
|
|
39
|
+
break;
|
|
40
|
+
case "subject":
|
|
41
|
+
for (const part of value.split(/\s+/))
|
|
42
|
+
if (part)
|
|
43
|
+
words.push(part);
|
|
44
|
+
break;
|
|
45
|
+
case "older": {
|
|
46
|
+
const days = /^(\d+)d$/i.exec(value)?.[1];
|
|
47
|
+
if (days === undefined)
|
|
48
|
+
throw bad(`older:${value} needs days, like older:365d`);
|
|
49
|
+
match.older_than_days = Number(days);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "larger":
|
|
53
|
+
match.larger_than_bytes = bytesOf(value);
|
|
54
|
+
break;
|
|
55
|
+
case "has":
|
|
56
|
+
if (!/^attachments?$/i.test(value))
|
|
57
|
+
throw bad(`has:${value}; only has:attachment is known`);
|
|
58
|
+
match.has_attachments = true;
|
|
59
|
+
break;
|
|
60
|
+
case "label":
|
|
61
|
+
match.label = value;
|
|
62
|
+
break;
|
|
63
|
+
case "is":
|
|
64
|
+
if (/^unread$/i.test(value))
|
|
65
|
+
match.unread = true;
|
|
66
|
+
else if (/^bulk$/i.test(value))
|
|
67
|
+
match.bulk = true;
|
|
68
|
+
else
|
|
69
|
+
throw bad(`is:${value}; is:unread and is:bulk are known`);
|
|
70
|
+
break;
|
|
71
|
+
case "score":
|
|
72
|
+
if (!/^-?\d+$/.test(value))
|
|
73
|
+
throw bad(`score:${value} needs a whole number`);
|
|
74
|
+
match.score_at_least = Number(value);
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
throw bad(`${field}: is not a field; use ${FILTER_HELP}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (words.length)
|
|
81
|
+
match.subject = words.join(" ");
|
|
82
|
+
return match;
|
|
83
|
+
};
|
|
84
|
+
export const ACTION_HELP = "one of skip (never read or store), envelope (store only sender, date, size and labels: counted, out of search, " +
|
|
85
|
+
"cleanable later), keep (store in full even when a skip or envelope rule matches) or protect (keep, and no " +
|
|
86
|
+
"cleanup may ever cut, archive or trash it); add tag:<name> to also tag the mail, e.g. envelope,tag:promo";
|
|
87
|
+
const ACTIONS = ["skip", "envelope", "keep", "protect"];
|
|
88
|
+
/** `envelope,tag:promo` → the store action, protection and tags. */
|
|
89
|
+
export const actionsOf = (value) => {
|
|
90
|
+
const parts = value
|
|
91
|
+
.split(",")
|
|
92
|
+
.map((part) => part.trim())
|
|
93
|
+
.filter(Boolean);
|
|
94
|
+
const tags = parts.filter((part) => part.startsWith("tag:")).map((part) => part.slice(4).trim());
|
|
95
|
+
const actions = parts.filter((part) => !part.startsWith("tag:"));
|
|
96
|
+
if (tags.some((tag) => tag === ""))
|
|
97
|
+
throw new CliError("validation_error", "--do: tag: needs a name, e.g. tag:promo");
|
|
98
|
+
if (actions.length !== 1 || !ACTIONS.includes(actions[0]))
|
|
99
|
+
throw new CliError("validation_error", `--do: choose ${ACTIONS.join(", ")}, plus any tag:<name>`);
|
|
100
|
+
const action = actions[0];
|
|
101
|
+
return { action: action === "protect" ? "keep" : action, protect: action === "protect", tags };
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/mail/rules/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type MailRule, type MailRuleMatch } from "@wirecat/cli-core/mail";
|
|
2
|
+
import type { MessageStore } from "@wirecat/cli-core/store";
|
|
3
|
+
export declare const saveRule: (store: MessageStore, address: string, rule: MailRule) => Promise<MailRule>;
|
|
4
|
+
export declare const removeRule: (store: MessageStore, address: string, match: MailRuleMatch) => Promise<boolean>;
|
|
5
|
+
export declare const ruleCounts: (store: MessageStore, address: string) => Promise<Map<number, {
|
|
6
|
+
emails: number;
|
|
7
|
+
bytes: number;
|
|
8
|
+
}>>;
|
|
9
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/mail/rules/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAA6B,MAAM,wBAAwB,CAAA;AACrG,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAQvE,eAAO,MAAM,QAAQ,UAAiB,YAAY,WAAW,MAAM,QAAQ,QAAQ,KAAG,OAAO,CAAC,QAAQ,CAGrG,CAAA;AAED,eAAO,MAAM,UAAU,UAAiB,YAAY,WAAW,MAAM,SAAS,aAAa,KAAG,OAAO,CAAC,OAAO,CAG5G,CAAA;AAED,eAAO,MAAM,UAAU,UAAiB,YAAY,WAAW,MAAM;;;GAMlE,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ruleInput, storedMailRule } from "@wirecat/cli-core/mail";
|
|
2
|
+
const accountKey = (address) => ({ provider: "email", account: address.toLowerCase() });
|
|
3
|
+
// Named by its address, as memo names it, so both tools keep one row for the mailbox.
|
|
4
|
+
const accountIdOf = (store, address) => store.accounts.saveAccount(accountKey(address), { name: address });
|
|
5
|
+
export const saveRule = async (store, address, rule) => {
|
|
6
|
+
const stored = await store.mail.rules.upsert(await accountIdOf(store, address), ruleInput(rule), Date.now());
|
|
7
|
+
return storedMailRule(stored) ?? rule;
|
|
8
|
+
};
|
|
9
|
+
export const removeRule = async (store, address, match) => {
|
|
10
|
+
const { conditions } = ruleInput({ match, action: "skip", decided_at: new Date().toISOString() });
|
|
11
|
+
return store.mail.rules.remove(await accountIdOf(store, address), conditions);
|
|
12
|
+
};
|
|
13
|
+
export const ruleCounts = async (store, address) => new Map((await store.mail.rules.counts(await accountIdOf(store, address))).map(({ ruleId, emails, bytes }) => [
|
|
14
|
+
ruleId,
|
|
15
|
+
{ emails, bytes },
|
|
16
|
+
]));
|
|
17
|
+
//# sourceMappingURL=store.js.map
|