proton-mail-bridge-client 2.16.2 → 2.17.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.
Files changed (245) hide show
  1. package/README.md +149 -12
  2. package/dist/cli.d.ts +16 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +178 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config-env.d.ts +2 -1
  7. package/dist/config-env.d.ts.map +1 -1
  8. package/dist/config-env.js +24 -0
  9. package/dist/config-env.js.map +1 -1
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +180 -11
  13. package/dist/index.js.map +1 -1
  14. package/dist/services/account-manager.d.ts +7 -0
  15. package/dist/services/account-manager.d.ts.map +1 -1
  16. package/dist/services/account-manager.js +59 -0
  17. package/dist/services/account-manager.js.map +1 -1
  18. package/dist/services/audit-service.d.ts +20 -0
  19. package/dist/services/audit-service.d.ts.map +1 -1
  20. package/dist/services/audit-service.js +266 -9
  21. package/dist/services/audit-service.js.map +1 -1
  22. package/dist/services/autoresponder-service.d.ts +35 -6
  23. package/dist/services/autoresponder-service.d.ts.map +1 -1
  24. package/dist/services/autoresponder-service.js +223 -108
  25. package/dist/services/autoresponder-service.js.map +1 -1
  26. package/dist/services/brief-service.d.ts +7 -1
  27. package/dist/services/brief-service.d.ts.map +1 -1
  28. package/dist/services/brief-service.js +19 -5
  29. package/dist/services/brief-service.js.map +1 -1
  30. package/dist/services/category-service.d.ts +91 -0
  31. package/dist/services/category-service.d.ts.map +1 -0
  32. package/dist/services/category-service.js +320 -0
  33. package/dist/services/category-service.js.map +1 -0
  34. package/dist/services/commitment-service.d.ts.map +1 -1
  35. package/dist/services/commitment-service.js +9 -3
  36. package/dist/services/commitment-service.js.map +1 -1
  37. package/dist/services/contact-service.d.ts +60 -0
  38. package/dist/services/contact-service.d.ts.map +1 -0
  39. package/dist/services/contact-service.js +248 -0
  40. package/dist/services/contact-service.js.map +1 -0
  41. package/dist/services/delivery-queue-service.d.ts +4 -0
  42. package/dist/services/delivery-queue-service.d.ts.map +1 -1
  43. package/dist/services/delivery-queue-service.js +12 -2
  44. package/dist/services/delivery-queue-service.js.map +1 -1
  45. package/dist/services/draft-store-service.d.ts.map +1 -1
  46. package/dist/services/draft-store-service.js +3 -2
  47. package/dist/services/draft-store-service.js.map +1 -1
  48. package/dist/services/embedding-service.d.ts +68 -0
  49. package/dist/services/embedding-service.d.ts.map +1 -0
  50. package/dist/services/embedding-service.js +465 -0
  51. package/dist/services/embedding-service.js.map +1 -0
  52. package/dist/services/event-log-service.d.ts +67 -0
  53. package/dist/services/event-log-service.d.ts.map +1 -0
  54. package/dist/services/event-log-service.js +280 -0
  55. package/dist/services/event-log-service.js.map +1 -0
  56. package/dist/services/grant-service.d.ts +84 -0
  57. package/dist/services/grant-service.d.ts.map +1 -0
  58. package/dist/services/grant-service.js +214 -0
  59. package/dist/services/grant-service.js.map +1 -0
  60. package/dist/services/local-index-service.d.ts +18 -1
  61. package/dist/services/local-index-service.d.ts.map +1 -1
  62. package/dist/services/local-index-service.js +54 -4
  63. package/dist/services/local-index-service.js.map +1 -1
  64. package/dist/services/reply-reminder-service.d.ts +4 -0
  65. package/dist/services/reply-reminder-service.d.ts.map +1 -1
  66. package/dist/services/reply-reminder-service.js +22 -2
  67. package/dist/services/reply-reminder-service.js.map +1 -1
  68. package/dist/services/rules-service.d.ts +9 -1
  69. package/dist/services/rules-service.d.ts.map +1 -1
  70. package/dist/services/rules-service.js +8 -3
  71. package/dist/services/rules-service.js.map +1 -1
  72. package/dist/services/sieve-export.d.ts +19 -0
  73. package/dist/services/sieve-export.d.ts.map +1 -0
  74. package/dist/services/sieve-export.js +192 -0
  75. package/dist/services/sieve-export.js.map +1 -0
  76. package/dist/services/simple-imap-service.d.ts +13 -0
  77. package/dist/services/simple-imap-service.d.ts.map +1 -1
  78. package/dist/services/simple-imap-service.js +142 -18
  79. package/dist/services/simple-imap-service.js.map +1 -1
  80. package/dist/services/smtp-service.d.ts +1 -0
  81. package/dist/services/smtp-service.d.ts.map +1 -1
  82. package/dist/services/smtp-service.js +20 -0
  83. package/dist/services/smtp-service.js.map +1 -1
  84. package/dist/services/snooze-service.d.ts +4 -0
  85. package/dist/services/snooze-service.d.ts.map +1 -1
  86. package/dist/services/snooze-service.js +12 -3
  87. package/dist/services/snooze-service.js.map +1 -1
  88. package/dist/services/template-service.d.ts.map +1 -1
  89. package/dist/services/template-service.js +3 -2
  90. package/dist/services/template-service.js.map +1 -1
  91. package/dist/services/triage-plan-service.d.ts +10 -2
  92. package/dist/services/triage-plan-service.d.ts.map +1 -1
  93. package/dist/services/triage-plan-service.js +23 -3
  94. package/dist/services/triage-plan-service.js.map +1 -1
  95. package/dist/tools/attachments.js +1 -1
  96. package/dist/tools/attachments.js.map +1 -1
  97. package/dist/tools/brief.d.ts.map +1 -1
  98. package/dist/tools/brief.js +14 -4
  99. package/dist/tools/brief.js.map +1 -1
  100. package/dist/tools/categories.d.ts +4 -0
  101. package/dist/tools/categories.d.ts.map +1 -0
  102. package/dist/tools/categories.js +269 -0
  103. package/dist/tools/categories.js.map +1 -0
  104. package/dist/tools/commitments.d.ts.map +1 -1
  105. package/dist/tools/commitments.js +6 -2
  106. package/dist/tools/commitments.js.map +1 -1
  107. package/dist/tools/contacts.d.ts +6 -0
  108. package/dist/tools/contacts.d.ts.map +1 -0
  109. package/dist/tools/contacts.js +295 -0
  110. package/dist/tools/contacts.js.map +1 -0
  111. package/dist/tools/context.d.ts +4 -0
  112. package/dist/tools/context.d.ts.map +1 -1
  113. package/dist/tools/context.js +3 -0
  114. package/dist/tools/context.js.map +1 -1
  115. package/dist/tools/definitions.d.ts +333 -5
  116. package/dist/tools/definitions.d.ts.map +1 -1
  117. package/dist/tools/definitions.js +169 -5
  118. package/dist/tools/definitions.js.map +1 -1
  119. package/dist/tools/delegation.d.ts +44 -0
  120. package/dist/tools/delegation.d.ts.map +1 -0
  121. package/dist/tools/delegation.js +180 -0
  122. package/dist/tools/delegation.js.map +1 -0
  123. package/dist/tools/diagnostics.d.ts.map +1 -1
  124. package/dist/tools/diagnostics.js +6 -0
  125. package/dist/tools/diagnostics.js.map +1 -1
  126. package/dist/tools/drafts-support.d.ts +2 -1
  127. package/dist/tools/drafts-support.d.ts.map +1 -1
  128. package/dist/tools/drafts-support.js +14 -6
  129. package/dist/tools/drafts-support.js.map +1 -1
  130. package/dist/tools/drafts.d.ts.map +1 -1
  131. package/dist/tools/drafts.js +22 -1
  132. package/dist/tools/drafts.js.map +1 -1
  133. package/dist/tools/events.d.ts +47 -0
  134. package/dist/tools/events.d.ts.map +1 -0
  135. package/dist/tools/events.js +240 -0
  136. package/dist/tools/events.js.map +1 -0
  137. package/dist/tools/reading.d.ts.map +1 -1
  138. package/dist/tools/reading.js +125 -4
  139. package/dist/tools/reading.js.map +1 -1
  140. package/dist/tools/resources.d.ts +4 -0
  141. package/dist/tools/resources.d.ts.map +1 -1
  142. package/dist/tools/resources.js +9 -0
  143. package/dist/tools/resources.js.map +1 -1
  144. package/dist/tools/rules-runner.d.ts.map +1 -1
  145. package/dist/tools/rules-runner.js +1 -0
  146. package/dist/tools/rules-runner.js.map +1 -1
  147. package/dist/tools/rules.d.ts +9 -0
  148. package/dist/tools/rules.d.ts.map +1 -1
  149. package/dist/tools/rules.js +50 -19
  150. package/dist/tools/rules.js.map +1 -1
  151. package/dist/tools/sending.d.ts.map +1 -1
  152. package/dist/tools/sending.js +4 -0
  153. package/dist/tools/sending.js.map +1 -1
  154. package/dist/tools/style.d.ts +2 -0
  155. package/dist/tools/style.d.ts.map +1 -1
  156. package/dist/tools/style.js +3 -3
  157. package/dist/tools/style.js.map +1 -1
  158. package/dist/tools/templates-and-reminders.d.ts +3 -0
  159. package/dist/tools/templates-and-reminders.d.ts.map +1 -1
  160. package/dist/tools/templates-and-reminders.js +31 -17
  161. package/dist/tools/templates-and-reminders.js.map +1 -1
  162. package/dist/tools/triage.d.ts.map +1 -1
  163. package/dist/tools/triage.js +33 -11
  164. package/dist/tools/triage.js.map +1 -1
  165. package/dist/types/index.d.ts +23 -0
  166. package/dist/types/index.d.ts.map +1 -1
  167. package/dist/utils/account-identity.d.ts.map +1 -1
  168. package/dist/utils/account-identity.js +3 -2
  169. package/dist/utils/account-identity.js.map +1 -1
  170. package/dist/utils/approval.d.ts +133 -0
  171. package/dist/utils/approval.d.ts.map +1 -0
  172. package/dist/utils/approval.js +507 -0
  173. package/dist/utils/approval.js.map +1 -0
  174. package/dist/utils/atomic-write.d.ts +1 -1
  175. package/dist/utils/atomic-write.d.ts.map +1 -1
  176. package/dist/utils/atomic-write.js +10 -3
  177. package/dist/utils/atomic-write.js.map +1 -1
  178. package/dist/utils/commitment-phrases.d.ts.map +1 -1
  179. package/dist/utils/commitment-phrases.js +31 -18
  180. package/dist/utils/commitment-phrases.js.map +1 -1
  181. package/dist/utils/contact-text.d.ts +8 -0
  182. package/dist/utils/contact-text.d.ts.map +1 -0
  183. package/dist/utils/contact-text.js +24 -0
  184. package/dist/utils/contact-text.js.map +1 -0
  185. package/dist/utils/delegation.d.ts +17 -0
  186. package/dist/utils/delegation.d.ts.map +1 -0
  187. package/dist/utils/delegation.js +72 -0
  188. package/dist/utils/delegation.js.map +1 -0
  189. package/dist/utils/file-lock.d.ts.map +1 -1
  190. package/dist/utils/file-lock.js +5 -4
  191. package/dist/utils/file-lock.js.map +1 -1
  192. package/dist/utils/mail-security.d.ts +9 -1
  193. package/dist/utils/mail-security.d.ts.map +1 -1
  194. package/dist/utils/mail-security.js +444 -62
  195. package/dist/utils/mail-security.js.map +1 -1
  196. package/dist/utils/ocr.d.ts +24 -0
  197. package/dist/utils/ocr.d.ts.map +1 -0
  198. package/dist/utils/ocr.js +109 -0
  199. package/dist/utils/ocr.js.map +1 -0
  200. package/dist/utils/office-text-worker.d.ts +2 -0
  201. package/dist/utils/office-text-worker.d.ts.map +1 -0
  202. package/dist/utils/office-text-worker.js +17 -0
  203. package/dist/utils/office-text-worker.js.map +1 -0
  204. package/dist/utils/office-text.d.ts +32 -0
  205. package/dist/utils/office-text.d.ts.map +1 -0
  206. package/dist/utils/office-text.js +540 -0
  207. package/dist/utils/office-text.js.map +1 -0
  208. package/dist/utils/pdf-text-worker.js +2 -2
  209. package/dist/utils/pdf-text-worker.js.map +1 -1
  210. package/dist/utils/pdf-text.d.ts +10 -0
  211. package/dist/utils/pdf-text.d.ts.map +1 -1
  212. package/dist/utils/pdf-text.js +49 -9
  213. package/dist/utils/pdf-text.js.map +1 -1
  214. package/dist/utils/png.d.ts +2 -0
  215. package/dist/utils/png.d.ts.map +1 -0
  216. package/dist/utils/png.js +26 -0
  217. package/dist/utils/png.js.map +1 -0
  218. package/dist/utils/regular-file.d.ts +9 -0
  219. package/dist/utils/regular-file.d.ts.map +1 -0
  220. package/dist/utils/regular-file.js +34 -0
  221. package/dist/utils/regular-file.js.map +1 -0
  222. package/dist/utils/runtime-policy.d.ts.map +1 -1
  223. package/dist/utils/runtime-policy.js +2 -0
  224. package/dist/utils/runtime-policy.js.map +1 -1
  225. package/dist/utils/safe-write.d.ts +8 -0
  226. package/dist/utils/safe-write.d.ts.map +1 -0
  227. package/dist/utils/safe-write.js +46 -0
  228. package/dist/utils/safe-write.js.map +1 -0
  229. package/dist/utils/server-state-paths.d.ts +12 -0
  230. package/dist/utils/server-state-paths.d.ts.map +1 -0
  231. package/dist/utils/server-state-paths.js +69 -0
  232. package/dist/utils/server-state-paths.js.map +1 -0
  233. package/dist/utils/style-profile.d.ts +18 -0
  234. package/dist/utils/style-profile.d.ts.map +1 -1
  235. package/dist/utils/style-profile.js +63 -14
  236. package/dist/utils/style-profile.js.map +1 -1
  237. package/dist/utils/vcard.d.ts +27 -0
  238. package/dist/utils/vcard.d.ts.map +1 -0
  239. package/dist/utils/vcard.js +141 -0
  240. package/dist/utils/vcard.js.map +1 -0
  241. package/dist/utils/zip-reader.d.ts +16 -0
  242. package/dist/utils/zip-reader.d.ts.map +1 -0
  243. package/dist/utils/zip-reader.js +108 -0
  244. package/dist/utils/zip-reader.js.map +1 -0
  245. package/package.json +3 -3
@@ -1,9 +1,10 @@
1
1
  import type { EmailSummary, ProtonMailConfig } from "../types/index.js";
2
2
  import { type Logger } from "../utils/logger.js";
3
+ import { type StyleProfile } from "../utils/style-profile.js";
3
4
  export type AutoresponderAction = "reply" | "draft" | "skip";
4
5
  export interface AutoresponderMatchRule {
5
6
  name?: string;
6
- /** Addresses, or "*@example.com" / "@example.com" for a whole domain. Any of them matches. */
7
+ /** Addresses, "*@example.com" / "@example.com" for a whole domain, or "tag:vip" for people with that contact tag (set_contact). Any of them matches. */
7
8
  from?: string[];
8
9
  /** The subject contains any of these (case-insensitive). */
9
10
  subjectContains?: string[];
@@ -19,6 +20,12 @@ export interface AutoresponderRule {
19
20
  /** Subject of the reply; {subject} is replaced by the subject of the message answered. */
20
21
  subject: string;
21
22
  body: string;
23
+ /** Subject and body by language code (en, pl, de), used when the message answered is in that language. */
24
+ subjects?: Record<string, string>;
25
+ bodies?: Record<string, string>;
26
+ /** Plain text for {returnDate} (a YYYY-MM-DD date is written out in the reply's language) and {backupContact}. */
27
+ returnDate?: string;
28
+ backupContact?: string;
22
29
  /** Only mail that arrived inside this window is answered, and the outgoing note shows only inside it. */
23
30
  startsAt?: string;
24
31
  endsAt?: string;
@@ -72,7 +79,7 @@ export interface AutoresponderState {
72
79
  log?: AutoresponderLogEntry[];
73
80
  }
74
81
  export declare const AUTORESPONDER_MAX_PER_DAY = 50;
75
- export type AutoresponderSkipReason = "not-active" | "wrong-folder" | "before-activation" | "outside-window" | "not-human" | "suspicious" | "no-sender" | "own-address" | "no-reply-sender" | "not-addressed-to-us" | "already-handled" | "replied-recently" | "daily-limit" | "rule-skip" | "answered-already" | "no-text" | "cannot-send-or-draft";
82
+ export type AutoresponderSkipReason = "not-active" | "wrong-folder" | "before-activation" | "outside-window" | "not-human" | "suspicious" | "no-sender" | "own-address" | "no-reply-sender" | "not-addressed-to-us" | "already-handled" | "replied-recently" | "daily-limit" | "rule-skip" | "answered-already" | "no-text" | "cannot-send-or-draft" | "lookup-failed";
76
83
  /** The weekday (0=Sunday) and minutes since midnight at `now` in `timezone`. */
77
84
  export declare function localClock(now: number, timezone?: string): {
78
85
  day: number;
@@ -87,9 +94,13 @@ export interface AutoresponderChecks {
87
94
  }
88
95
  /** Why a message must not get a reply, or undefined when it may. Pure: nothing is recorded here. */
89
96
  export declare function skipReason(email: EmailSummary, state: AutoresponderState, ownAddresses: string[], now: number, checks?: AutoresponderChecks): AutoresponderSkipReason | undefined;
90
- /** Fills {subject}, {senderName}, {senderFirstName}, {senderAddress}, {startsAt}, {endsAt}; other braces stay as written. */
91
- export declare function renderText(template: string, rule: AutoresponderRule, email: EmailSummary): string;
92
- export declare function replySubject(rule: AutoresponderRule, original: string, email?: EmailSummary): string;
97
+ /**
98
+ * Fills {subject}, {senderName}, {senderFirstName}, {senderAddress}, {startsAt}, {endsAt}, {returnDate}, {backupContact};
99
+ * other braces stay as written. Values are inserted as they are, never read as a template. `language` writes the dates
100
+ * in that language (default English).
101
+ */
102
+ export declare function renderText(template: string, rule: AutoresponderRule, email: EmailSummary, language?: string): string;
103
+ export declare function replySubject(rule: AutoresponderRule, original: string, email?: EmailSummary, language?: string): string;
93
104
  export interface AutoresponderReply {
94
105
  to: string;
95
106
  subject: string;
@@ -110,6 +121,10 @@ export interface AutoresponderDeps {
110
121
  canDraft?: boolean;
111
122
  isKnownContact?: (address: string) => Promise<boolean>;
112
123
  hasAnsweredSince?: (address: string, sinceIso: string) => Promise<boolean>;
124
+ /** The contact tags of an address, for "tag:vip" in a rule's from. */
125
+ contactTags?: (address: string) => Promise<string[]>;
126
+ /** Your style profile, if one was made (get_style_profile); a draft then opens and closes the way you do. */
127
+ styleProfile?: () => Promise<StyleProfile | undefined>;
113
128
  }
114
129
  export interface AutoresponderDecision {
115
130
  id: string;
@@ -119,6 +134,12 @@ export interface AutoresponderDecision {
119
134
  reason?: AutoresponderSkipReason;
120
135
  rule?: string;
121
136
  reply?: AutoresponderReply;
137
+ /** The language the message answered is in, when it could be told. */
138
+ language?: string;
139
+ /** Where the text came from: the matching rule's own text, the text for the sender's language, or the default. */
140
+ textFrom?: "rule" | "language" | "default";
141
+ /** A draft given your greeting and closing from the style profile. */
142
+ styled?: boolean;
122
143
  }
123
144
  /** Judges one message. Pure apart from the lookups in `deps`; records nothing. */
124
145
  export declare function decide(email: EmailSummary, state: AutoresponderState, ownAddresses: string[], now: number, deps: AutoresponderDeps, checks?: AutoresponderChecks): Promise<AutoresponderDecision>;
@@ -126,6 +147,12 @@ export interface AutoresponderPatch {
126
147
  enabled?: boolean;
127
148
  subject?: string;
128
149
  body?: string;
150
+ /** Replace the whole map ({} or null clears it); an empty text removes that language. */
151
+ subjects?: unknown;
152
+ bodies?: unknown;
153
+ /** Empty string removes it. */
154
+ returnDate?: string;
155
+ backupContact?: string;
129
156
  startsAt?: string;
130
157
  endsAt?: string;
131
158
  replyEveryDays?: number;
@@ -170,8 +197,10 @@ export declare class AutoresponderService {
170
197
  };
171
198
  recent: AutoresponderLogEntry[];
172
199
  }>;
200
+ /** The rule as it would be after `set(patch)`, without storing anything (what a question to the person describes). */
201
+ previewSet(patch: AutoresponderPatch, now?: number): Promise<AutoresponderRule>;
173
202
  /** Create or change the rule. Switching it on (from off) restarts "mail from before this moment is not answered". */
174
- set(patch: AutoresponderPatch, now?: number): Promise<AutoresponderRule>;
203
+ set(patch: AutoresponderPatch, now?: number, verify?: (next: AutoresponderRule) => void): Promise<AutoresponderRule>;
175
204
  /** What it would do with `emails`, judged as if the rule were on now. Records nothing and sends nothing. */
176
205
  preview(emails: EmailSummary[], ownAddresses: string[], deps?: AutoresponderDeps, now?: number): Promise<AutoresponderDecision[]>;
177
206
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"autoresponder-service.d.ts","sourceRoot":"","sources":["../../src/services/autoresponder-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"autoresponder-service.d.ts","sourceRoot":"","sources":["../../src/services/autoresponder-service.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { mkdir, readFile } from "node:fs/promises";
1
+ import { mkdir } from "node:fs/promises";
2
+ import { readRegularFile } from "../utils/regular-file.js";
2
3
  import { dirname, join } from "node:path";
3
4
  import { writeFileAtomic } from "../utils/atomic-write.js";
4
5
  import { isFileNotFound, setAsideCorruptStore } from "../utils/corrupt-store.js";
@@ -6,6 +7,7 @@ import { withFileLock } from "../utils/file-lock.js";
6
7
  import { InvalidArgumentError } from "../utils/helpers.js";
7
8
  import { logger } from "../utils/logger.js";
8
9
  import { assessMail } from "../utils/mail-security.js";
10
+ import { detectLanguage, greetingAndClosing, hasClosing, hasGreeting, STYLE_LANGUAGES } from "../utils/style-profile.js";
9
11
  export const AUTORESPONDER_MAX_PER_DAY = 50;
10
12
  const MAX_DAILY_LIMIT = 200;
11
13
  const MAX_RULES = 20;
@@ -105,10 +107,12 @@ export function skipReason(email, state, ownAddresses, now, checks = {}) {
105
107
  return "daily-limit";
106
108
  return undefined;
107
109
  }
108
- function matchesAddress(pattern, address) {
110
+ function matchesAddress(pattern, address, tags = []) {
109
111
  const p = pattern.trim().toLowerCase();
110
112
  if (!p)
111
113
  return false;
114
+ if (p.startsWith("tag:"))
115
+ return tags.includes(p.slice(4).trim());
112
116
  if (p.startsWith("*@") || p.startsWith("@"))
113
117
  return address.endsWith(p.slice(p.indexOf("@")));
114
118
  return address === p;
@@ -119,34 +123,44 @@ function firstName(display, address) {
119
123
  return name.split(/\s+/)[0];
120
124
  return address.split("@")[0].split(/[._+-]/)[0];
121
125
  }
122
- function formatDate(iso, timezone) {
126
+ const DATE_LOCALES = { en: "en-GB", pl: "pl-PL", de: "de-DE" };
127
+ const PLAIN_DATE = /^\d{4}-\d{2}-\d{2}$/;
128
+ function formatDate(iso, timezone, language) {
123
129
  if (!iso)
124
130
  return "";
125
131
  try {
126
- return new Intl.DateTimeFormat("en-GB", { timeZone: timezone, day: "numeric", month: "long", year: "numeric" }).format(new Date(iso));
132
+ return new Intl.DateTimeFormat(DATE_LOCALES[language ?? ""] ?? "en-GB", { timeZone: timezone, day: "numeric", month: "long", year: "numeric" }).format(new Date(iso));
127
133
  }
128
134
  catch {
129
135
  return iso.slice(0, 10);
130
136
  }
131
137
  }
132
- /** Fills {subject}, {senderName}, {senderFirstName}, {senderAddress}, {startsAt}, {endsAt}; other braces stay as written. */
133
- export function renderText(template, rule, email) {
138
+ /**
139
+ * Fills {subject}, {senderName}, {senderFirstName}, {senderAddress}, {startsAt}, {endsAt}, {returnDate}, {backupContact};
140
+ * other braces stay as written. Values are inserted as they are, never read as a template. `language` writes the dates
141
+ * in that language (default English).
142
+ */
143
+ export function renderText(template, rule, email, language) {
134
144
  const address = email.from[0]?.address ?? "";
135
145
  const display = email.from[0]?.name;
146
+ const returnDate = rule.returnDate ?? "";
136
147
  const values = {
137
148
  subject: email.subject.replace(/^(re|aw|odp)\s*:\s*/i, "").trim() || "(no subject)",
138
149
  senderName: (display ?? "").replace(/["<>]/g, "").trim() || address,
139
150
  senderFirstName: firstName(display, address),
140
151
  senderAddress: address,
141
- startsAt: formatDate(rule.startsAt, rule.timezone),
142
- endsAt: formatDate(rule.endsAt, rule.timezone),
152
+ startsAt: formatDate(rule.startsAt, rule.timezone, language),
153
+ endsAt: formatDate(rule.endsAt, rule.timezone, language),
154
+ // A plain date names a day, not a moment, so it is not shifted by a time zone.
155
+ returnDate: PLAIN_DATE.test(returnDate) ? formatDate(`${returnDate}T00:00:00Z`, "UTC", language) : returnDate,
156
+ backupContact: rule.backupContact ?? "",
143
157
  };
144
- return template.replace(/\{(subject|senderName|senderFirstName|senderAddress|startsAt|endsAt)\}/g, (_, key) => values[key]);
158
+ return template.replace(/\{(subject|senderName|senderFirstName|senderAddress|startsAt|endsAt|returnDate|backupContact)\}/g, (_, key) => values[key]);
145
159
  }
146
- export function replySubject(rule, original, email) {
160
+ export function replySubject(rule, original, email, language) {
147
161
  const template = rule.subject || DEFAULT_SUBJECT;
148
162
  const stripped = original.replace(/^(re|aw|odp)\s*:\s*/i, "").trim() || "(no subject)";
149
- const subject = email ? renderText(template, rule, { ...email, subject: original }) : template.replace(/\{subject\}/g, stripped);
163
+ const subject = email ? renderText(template, rule, { ...email, subject: original }, language) : template.replace(/\{subject\}/g, () => stripped);
150
164
  return subject.replace(/[\r\n]+/g, " ").slice(0, 200);
151
165
  }
152
166
  /** Judges one message. Pure apart from the lookups in `deps`; records nothing. */
@@ -165,25 +179,57 @@ export async function decide(email, state, ownAddresses, now, deps, checks = {})
165
179
  let subjectTemplate = rule.subject;
166
180
  let bodyTemplate = rule.body;
167
181
  let ruleName;
182
+ let ruleSubject = false;
183
+ let ruleBody = false;
168
184
  const needsKnown = rule.rules?.some((candidate) => candidate.known) === true;
169
- const known = needsKnown && deps.isKnownContact ? await deps.isKnownContact(sender).catch(() => false) : undefined;
185
+ // A lookup that throws is not an answer: a rule that depends on it cannot be judged, so it must not be skipped over.
186
+ let knownFailed = false;
187
+ let tagsFailed = false;
188
+ const known = needsKnown && deps.isKnownContact ? await deps.isKnownContact(sender).catch(() => {
189
+ knownFailed = true;
190
+ return false;
191
+ }) : undefined;
192
+ const needsTags = rule.rules?.some((candidate) => candidate.from?.some((pattern) => pattern.trim().toLowerCase().startsWith("tag:"))) === true;
193
+ const tags = needsTags && deps.contactTags ? await deps.contactTags(sender).catch(() => {
194
+ tagsFailed = true;
195
+ return [];
196
+ }) : [];
170
197
  for (const [index, candidate] of (rule.rules ?? []).entries()) {
171
- if (candidate.from?.length && !candidate.from.some((pattern) => matchesAddress(pattern, sender)))
172
- continue;
198
+ let unsure = false;
199
+ if (candidate.from?.length && !candidate.from.some((pattern) => matchesAddress(pattern, sender, tags))) {
200
+ if (tagsFailed && candidate.from.some((pattern) => pattern.trim().toLowerCase().startsWith("tag:")))
201
+ unsure = true;
202
+ else
203
+ continue;
204
+ }
173
205
  if (candidate.subjectContains?.length && !candidate.subjectContains.some((text) => email.subject.toLowerCase().includes(text.toLowerCase())))
174
206
  continue;
175
- if (candidate.known && (known === undefined || (candidate.known === "yes") !== known))
207
+ if (candidate.known && knownFailed)
208
+ unsure = true;
209
+ else if (candidate.known && (known === undefined || (candidate.known === "yes") !== known))
176
210
  continue;
211
+ // Whatever this rule says (skip, say) cannot be told, and the default reply is not what its author meant.
212
+ if (unsure)
213
+ return { ...base, outcome: "skip", reason: "lookup-failed", rule: candidate.name || `rule ${index + 1}` };
177
214
  action = candidate.action;
178
215
  ruleName = candidate.name || `rule ${index + 1}`;
179
216
  if (candidate.subject !== undefined)
180
- subjectTemplate = candidate.subject;
217
+ [subjectTemplate, ruleSubject] = [candidate.subject, true];
181
218
  if (candidate.body !== undefined)
182
- bodyTemplate = candidate.body;
219
+ [bodyTemplate, ruleBody] = [candidate.body, true];
183
220
  break;
184
221
  }
185
222
  if (action === "skip")
186
223
  return { ...base, outcome: "skip", reason: "rule-skip", rule: ruleName };
224
+ // The sender's language picks the text where the matching rule has none of its own.
225
+ const language = detectLanguage(`${email.subject}\n${email.preview ?? ""}`.slice(0, 2000));
226
+ let textFrom = ruleBody ? "rule" : "default";
227
+ let bodyLanguage;
228
+ let subjectLanguage;
229
+ if (language && !ruleBody && rule.bodies?.[language])
230
+ [bodyTemplate, bodyLanguage, textFrom] = [rule.bodies[language], language, "language"];
231
+ if (language && !ruleSubject && rule.subjects?.[language])
232
+ [subjectTemplate, subjectLanguage] = [rule.subjects[language], language];
187
233
  if (!bodyTemplate?.trim())
188
234
  return { ...base, outcome: "skip", reason: "no-text", rule: ruleName };
189
235
  // What the server may do: a reply it may not send becomes a draft; with neither, nothing.
@@ -195,20 +241,38 @@ export async function decide(email, state, ownAddresses, now, deps, checks = {})
195
241
  return { ...base, outcome: "skip", reason: "cannot-send-or-draft", rule: ruleName };
196
242
  if (rule.skipIfAnswered !== false && deps.hasAnsweredSince) {
197
243
  const since = email.internalDate ?? email.date ?? new Date(now).toISOString();
198
- if (await deps.hasAnsweredSince(sender, since).catch(() => false))
244
+ // A lookup that throws is not "no": the sender may already have been answered, so wait for the next sync.
245
+ const answered = await deps.hasAnsweredSince(sender, since).catch(() => undefined);
246
+ if (answered === undefined)
247
+ return { ...base, outcome: "skip", reason: "lookup-failed", rule: ruleName };
248
+ if (answered)
199
249
  return { ...base, outcome: "skip", reason: "answered-already", rule: ruleName };
200
250
  }
251
+ let body = renderText(bodyTemplate, rule, email, bodyLanguage);
252
+ let styled = false;
253
+ // Only a draft: it is read before it goes, while a sent reply carries exactly the text that was set.
254
+ const replyLanguage = bodyLanguage ?? detectLanguage(body);
255
+ if (action === "draft" && deps.styleProfile && replyLanguage) {
256
+ const profile = await deps.styleProfile().catch(() => undefined);
257
+ if (profile) {
258
+ const { greeting, closing } = greetingAndClosing(profile, sender, replyLanguage);
259
+ const open = greeting && !hasGreeting(body) ? `${greeting.replace("{name}", () => firstName(email.from[0]?.name, email.from[0]?.address ?? sender))}\n\n` : "";
260
+ const close = closing && !hasClosing(body) ? `\n\n${closing}` : "";
261
+ if (open || close)
262
+ [body, styled] = [`${open}${body}${close}`, true];
263
+ }
264
+ }
201
265
  const reply = {
202
266
  to: sender,
203
- subject: replySubject({ ...rule, subject: subjectTemplate }, email.subject, email),
204
- body: renderText(bodyTemplate, rule, email),
267
+ subject: replySubject({ ...rule, subject: subjectTemplate }, email.subject, email, subjectLanguage),
268
+ body,
205
269
  inReplyTo: email.messageId,
206
270
  references: [...(email.references ?? []), ...(email.messageId ? [email.messageId] : [])],
207
271
  headers: { "Auto-Submitted": "auto-replied", Precedence: "bulk", "X-Auto-Response-Suppress": "All" },
208
272
  sourceEmailId: email.id,
209
273
  rule: ruleName,
210
274
  };
211
- return { ...base, outcome: action, reply, rule: ruleName };
275
+ return { ...base, outcome: action, reply, rule: ruleName, language, textFrom, ...(styled ? { styled } : {}) };
212
276
  }
213
277
  function validateRules(rules) {
214
278
  if (!Array.isArray(rules))
@@ -274,6 +338,136 @@ function validateOutsideHours(value) {
274
338
  }
275
339
  return { days: [...new Set(hours.days)].sort(), from: hours.from, to: hours.to };
276
340
  }
341
+ // Only the languages the detector can tell apart: a text for any other could never be chosen.
342
+ function validateLanguageTexts(value, name, max) {
343
+ if (value === null)
344
+ return undefined;
345
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
346
+ throw new InvalidArgumentError(`${name} must be an object keyed by language code (${STYLE_LANGUAGES.join(", ")}), or null to remove it.`);
347
+ }
348
+ const out = {};
349
+ for (const [key, text] of Object.entries(value)) {
350
+ const code = key.trim().toLowerCase();
351
+ if (!STYLE_LANGUAGES.includes(code))
352
+ throw new InvalidArgumentError(`${name}: "${key.slice(0, 20)}" is not a language that can be recognised; use ${STYLE_LANGUAGES.join(", ")}.`);
353
+ if (typeof text !== "string" || text.length > max)
354
+ throw new InvalidArgumentError(`${name}.${code} must be text up to ${max} characters.`);
355
+ if (text.trim())
356
+ out[code] = text.trim();
357
+ }
358
+ return Object.keys(out).length ? out : undefined;
359
+ }
360
+ function validateLine(value, name, max) {
361
+ if (value.length > max || /[\r\n]/.test(value))
362
+ throw new InvalidArgumentError(`${name} must be one line of up to ${max} characters.`);
363
+ return value.trim() || undefined;
364
+ }
365
+ /** What the rule becomes when `patch` is applied to `before`. Pure: nothing is stored. */
366
+ function mergePatch(before, patch, now) {
367
+ const next = {
368
+ enabled: before?.enabled ?? false,
369
+ subject: before?.subject ?? DEFAULT_SUBJECT,
370
+ body: before?.body ?? "",
371
+ subjects: before?.subjects,
372
+ bodies: before?.bodies,
373
+ returnDate: before?.returnDate,
374
+ backupContact: before?.backupContact,
375
+ startsAt: before?.startsAt,
376
+ endsAt: before?.endsAt,
377
+ replyEveryDays: before?.replyEveryDays ?? 7,
378
+ outgoingNote: before?.outgoingNote,
379
+ updatedAt: new Date(now).toISOString(),
380
+ activatedAt: before?.activatedAt,
381
+ mode: before?.mode,
382
+ timezone: before?.timezone,
383
+ outsideHours: before?.outsideHours,
384
+ dailyLimit: before?.dailyLimit,
385
+ skipIfAnswered: before?.skipIfAnswered,
386
+ rules: before?.rules,
387
+ };
388
+ if (patch.enabled !== undefined)
389
+ next.enabled = patch.enabled;
390
+ if (patch.subject !== undefined) {
391
+ if (patch.subject.length > 200)
392
+ throw new InvalidArgumentError("subject is limited to 200 characters.");
393
+ next.subject = patch.subject.trim() || DEFAULT_SUBJECT;
394
+ }
395
+ if (patch.body !== undefined) {
396
+ if (patch.body.length > 4000)
397
+ throw new InvalidArgumentError("body is limited to 4000 characters.");
398
+ next.body = patch.body.trim();
399
+ }
400
+ if (patch.subjects !== undefined)
401
+ next.subjects = validateLanguageTexts(patch.subjects, "subjects", 200);
402
+ if (patch.bodies !== undefined)
403
+ next.bodies = validateLanguageTexts(patch.bodies, "bodies", 4000);
404
+ if (patch.returnDate !== undefined) {
405
+ next.returnDate = validateLine(patch.returnDate, "returnDate", 100);
406
+ const day = next.returnDate && PLAIN_DATE.test(next.returnDate) ? Date.parse(`${next.returnDate}T00:00:00Z`) : undefined;
407
+ if (day !== undefined && (!Number.isFinite(day) || new Date(day).toISOString().slice(0, 10) !== next.returnDate)) {
408
+ throw new InvalidArgumentError(`returnDate "${next.returnDate}" is not a real date.`);
409
+ }
410
+ }
411
+ if (patch.backupContact !== undefined)
412
+ next.backupContact = validateLine(patch.backupContact, "backupContact", 200);
413
+ if (patch.startsAt !== undefined)
414
+ next.startsAt = parseDate(patch.startsAt, "startsAt");
415
+ if (patch.endsAt !== undefined)
416
+ next.endsAt = parseDate(patch.endsAt, "endsAt");
417
+ if (patch.replyEveryDays !== undefined) {
418
+ if (!Number.isInteger(patch.replyEveryDays) || patch.replyEveryDays < 1 || patch.replyEveryDays > 90) {
419
+ throw new InvalidArgumentError("replyEveryDays must be a whole number from 1 to 90.");
420
+ }
421
+ next.replyEveryDays = patch.replyEveryDays;
422
+ }
423
+ if (patch.outgoingNote !== undefined) {
424
+ if (patch.outgoingNote.length > 1000)
425
+ throw new InvalidArgumentError("outgoingNote is limited to 1000 characters.");
426
+ next.outgoingNote = patch.outgoingNote.trim() || undefined;
427
+ }
428
+ if (patch.mode !== undefined) {
429
+ if (patch.mode !== "send" && patch.mode !== "draft")
430
+ throw new InvalidArgumentError('mode must be "send" or "draft".');
431
+ next.mode = patch.mode;
432
+ }
433
+ if (patch.timezone !== undefined) {
434
+ if (patch.timezone === "")
435
+ next.timezone = undefined;
436
+ else {
437
+ try {
438
+ new Intl.DateTimeFormat("en-US", { timeZone: patch.timezone });
439
+ }
440
+ catch {
441
+ throw new InvalidArgumentError(`timezone must be an IANA name such as Europe/Warsaw (got "${patch.timezone}").`);
442
+ }
443
+ next.timezone = patch.timezone;
444
+ }
445
+ }
446
+ if (patch.outsideHours !== undefined)
447
+ next.outsideHours = validateOutsideHours(patch.outsideHours);
448
+ if (patch.dailyLimit !== undefined) {
449
+ if (!Number.isInteger(patch.dailyLimit) || patch.dailyLimit < 1 || patch.dailyLimit > MAX_DAILY_LIMIT) {
450
+ throw new InvalidArgumentError(`dailyLimit must be a whole number from 1 to ${MAX_DAILY_LIMIT}.`);
451
+ }
452
+ next.dailyLimit = patch.dailyLimit;
453
+ }
454
+ if (patch.skipIfAnswered !== undefined)
455
+ next.skipIfAnswered = patch.skipIfAnswered;
456
+ if (patch.rules !== undefined)
457
+ next.rules = validateRules(patch.rules);
458
+ if (next.rules && next.rules.length === 0)
459
+ next.rules = undefined;
460
+ if (next.startsAt && next.endsAt && Date.parse(next.endsAt) <= Date.parse(next.startsAt)) {
461
+ throw new InvalidArgumentError("endsAt must be after startsAt.");
462
+ }
463
+ const hasText = Boolean(next.body) || Boolean(next.bodies) || Boolean(next.outgoingNote) || Boolean(next.rules?.some((rule) => rule.action !== "skip" && rule.body));
464
+ if (next.enabled && !hasText) {
465
+ throw new InvalidArgumentError("Give a body (the reply), a rule with a body, or an outgoingNote before switching the autoresponder on.");
466
+ }
467
+ if (next.enabled && !before?.enabled)
468
+ next.activatedAt = new Date(now).toISOString();
469
+ return next;
470
+ }
277
471
  export class AutoresponderService {
278
472
  log;
279
473
  storePath;
@@ -333,96 +527,17 @@ export class AutoresponderService {
333
527
  recent: log.slice(-20).reverse(),
334
528
  };
335
529
  }
530
+ /** The rule as it would be after `set(patch)`, without storing anything (what a question to the person describes). */
531
+ async previewSet(patch, now = Date.now()) {
532
+ return mergePatch((await this.load()).rule, patch, now);
533
+ }
336
534
  /** Create or change the rule. Switching it on (from off) restarts "mail from before this moment is not answered". */
337
- async set(patch, now = Date.now()) {
535
+ async set(patch, now = Date.now(), verify) {
338
536
  return this.withLock(async () => {
339
537
  const state = await this.loadUnlocked();
340
538
  const before = state.rule;
341
- const next = {
342
- enabled: before?.enabled ?? false,
343
- subject: before?.subject ?? DEFAULT_SUBJECT,
344
- body: before?.body ?? "",
345
- startsAt: before?.startsAt,
346
- endsAt: before?.endsAt,
347
- replyEveryDays: before?.replyEveryDays ?? 7,
348
- outgoingNote: before?.outgoingNote,
349
- updatedAt: new Date(now).toISOString(),
350
- activatedAt: before?.activatedAt,
351
- mode: before?.mode,
352
- timezone: before?.timezone,
353
- outsideHours: before?.outsideHours,
354
- dailyLimit: before?.dailyLimit,
355
- skipIfAnswered: before?.skipIfAnswered,
356
- rules: before?.rules,
357
- };
358
- if (patch.enabled !== undefined)
359
- next.enabled = patch.enabled;
360
- if (patch.subject !== undefined) {
361
- if (patch.subject.length > 200)
362
- throw new InvalidArgumentError("subject is limited to 200 characters.");
363
- next.subject = patch.subject.trim() || DEFAULT_SUBJECT;
364
- }
365
- if (patch.body !== undefined) {
366
- if (patch.body.length > 4000)
367
- throw new InvalidArgumentError("body is limited to 4000 characters.");
368
- next.body = patch.body.trim();
369
- }
370
- if (patch.startsAt !== undefined)
371
- next.startsAt = parseDate(patch.startsAt, "startsAt");
372
- if (patch.endsAt !== undefined)
373
- next.endsAt = parseDate(patch.endsAt, "endsAt");
374
- if (patch.replyEveryDays !== undefined) {
375
- if (!Number.isInteger(patch.replyEveryDays) || patch.replyEveryDays < 1 || patch.replyEveryDays > 90) {
376
- throw new InvalidArgumentError("replyEveryDays must be a whole number from 1 to 90.");
377
- }
378
- next.replyEveryDays = patch.replyEveryDays;
379
- }
380
- if (patch.outgoingNote !== undefined) {
381
- if (patch.outgoingNote.length > 1000)
382
- throw new InvalidArgumentError("outgoingNote is limited to 1000 characters.");
383
- next.outgoingNote = patch.outgoingNote.trim() || undefined;
384
- }
385
- if (patch.mode !== undefined) {
386
- if (patch.mode !== "send" && patch.mode !== "draft")
387
- throw new InvalidArgumentError('mode must be "send" or "draft".');
388
- next.mode = patch.mode;
389
- }
390
- if (patch.timezone !== undefined) {
391
- if (patch.timezone === "")
392
- next.timezone = undefined;
393
- else {
394
- try {
395
- new Intl.DateTimeFormat("en-US", { timeZone: patch.timezone });
396
- }
397
- catch {
398
- throw new InvalidArgumentError(`timezone must be an IANA name such as Europe/Warsaw (got "${patch.timezone}").`);
399
- }
400
- next.timezone = patch.timezone;
401
- }
402
- }
403
- if (patch.outsideHours !== undefined)
404
- next.outsideHours = validateOutsideHours(patch.outsideHours);
405
- if (patch.dailyLimit !== undefined) {
406
- if (!Number.isInteger(patch.dailyLimit) || patch.dailyLimit < 1 || patch.dailyLimit > MAX_DAILY_LIMIT) {
407
- throw new InvalidArgumentError(`dailyLimit must be a whole number from 1 to ${MAX_DAILY_LIMIT}.`);
408
- }
409
- next.dailyLimit = patch.dailyLimit;
410
- }
411
- if (patch.skipIfAnswered !== undefined)
412
- next.skipIfAnswered = patch.skipIfAnswered;
413
- if (patch.rules !== undefined)
414
- next.rules = validateRules(patch.rules);
415
- if (next.rules && next.rules.length === 0)
416
- next.rules = undefined;
417
- if (next.startsAt && next.endsAt && Date.parse(next.endsAt) <= Date.parse(next.startsAt)) {
418
- throw new InvalidArgumentError("endsAt must be after startsAt.");
419
- }
420
- const hasText = Boolean(next.body) || Boolean(next.outgoingNote) || Boolean(next.rules?.some((rule) => rule.action !== "skip" && rule.body));
421
- if (next.enabled && !hasText) {
422
- throw new InvalidArgumentError("Give a body (the reply), a rule with a body, or an outgoingNote before switching the autoresponder on.");
423
- }
424
- if (next.enabled && !before?.enabled)
425
- next.activatedAt = new Date(now).toISOString();
539
+ const next = mergePatch(before, patch, now);
540
+ verify?.(next);
426
541
  state.rule = next;
427
542
  await this.save(state);
428
543
  this.cache = { rule: next, loadedAt: Date.now() };
@@ -549,7 +664,7 @@ export class AutoresponderService {
549
664
  }
550
665
  async loadUnlocked() {
551
666
  try {
552
- const parsed = JSON.parse(await readFile(this.storePath, "utf8"));
667
+ const parsed = JSON.parse(await readRegularFile(this.storePath));
553
668
  return {
554
669
  ...emptyState(),
555
670
  ...parsed,