@postalsys/email-content 0.1.8 → 0.1.10
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/README.md +27 -17
- package/dist/.tsbuildinfo +1 -1
- package/dist/analyze.d.ts +3 -0
- package/dist/analyze.d.ts.map +1 -1
- package/dist/analyze.js +10 -4
- package/dist/analyze.js.map +1 -1
- package/dist/annotate/agreement.d.ts +99 -0
- package/dist/annotate/agreement.d.ts.map +1 -0
- package/dist/annotate/agreement.js +126 -0
- package/dist/annotate/agreement.js.map +1 -0
- package/dist/annotate/client.d.ts +7 -19
- package/dist/annotate/client.d.ts.map +1 -1
- package/dist/annotate/client.js +28 -82
- package/dist/annotate/client.js.map +1 -1
- package/dist/annotate/http.d.ts +57 -0
- package/dist/annotate/http.d.ts.map +1 -0
- package/dist/annotate/http.js +99 -0
- package/dist/annotate/http.js.map +1 -0
- package/dist/annotate/llm.d.ts +17 -0
- package/dist/annotate/llm.d.ts.map +1 -1
- package/dist/annotate/llm.js +68 -36
- package/dist/annotate/llm.js.map +1 -1
- package/dist/annotate/probabilities.d.ts +48 -0
- package/dist/annotate/probabilities.d.ts.map +1 -0
- package/dist/annotate/probabilities.js +119 -0
- package/dist/annotate/probabilities.js.map +1 -0
- package/dist/annotate/providers.d.ts +26 -0
- package/dist/annotate/providers.d.ts.map +1 -0
- package/dist/annotate/providers.js +43 -0
- package/dist/annotate/providers.js.map +1 -0
- package/dist/annotate/store.d.ts +3 -0
- package/dist/annotate/store.d.ts.map +1 -1
- package/dist/annotate/store.js +3 -2
- package/dist/annotate/store.js.map +1 -1
- package/dist/annotate/typesafe-client.d.ts +64 -0
- package/dist/annotate/typesafe-client.d.ts.map +1 -0
- package/dist/annotate/typesafe-client.js +76 -0
- package/dist/annotate/typesafe-client.js.map +1 -0
- package/dist/annotate/typesafe-prompt.d.ts +64 -0
- package/dist/annotate/typesafe-prompt.d.ts.map +1 -0
- package/dist/annotate/typesafe-prompt.js +170 -0
- package/dist/annotate/typesafe-prompt.js.map +1 -0
- package/dist/classify/rules.d.ts +7 -0
- package/dist/classify/rules.d.ts.map +1 -1
- package/dist/classify/rules.js +117 -18
- package/dist/classify/rules.js.map +1 -1
- package/dist/cli/agreement.d.ts +27 -0
- package/dist/cli/agreement.d.ts.map +1 -0
- package/dist/cli/agreement.js +153 -0
- package/dist/cli/agreement.js.map +1 -0
- package/dist/cli/annotate.d.ts.map +1 -1
- package/dist/cli/annotate.js +28 -16
- package/dist/cli/annotate.js.map +1 -1
- package/dist/cli/audit.d.ts +12 -0
- package/dist/cli/audit.d.ts.map +1 -0
- package/dist/cli/audit.js +142 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/disagreements.d.ts.map +1 -1
- package/dist/cli/disagreements.js +20 -3
- package/dist/cli/disagreements.js.map +1 -1
- package/dist/cli/evaluate.d.ts.map +1 -1
- package/dist/cli/evaluate.js +31 -72
- package/dist/cli/evaluate.js.map +1 -1
- package/dist/cli/main.js +28 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/corpus/analyze.d.ts.map +1 -1
- package/dist/corpus/analyze.js +4 -5
- package/dist/corpus/analyze.js.map +1 -1
- package/dist/corpus/migrations/index.d.ts.map +1 -1
- package/dist/corpus/migrations/index.js +14 -0
- package/dist/corpus/migrations/index.js.map +1 -1
- package/dist/corpus/stats.d.ts +1 -0
- package/dist/corpus/stats.d.ts.map +1 -1
- package/dist/corpus/stats.js +1 -0
- package/dist/corpus/stats.js.map +1 -1
- package/dist/detect/patterns/forward-headers.json +24 -0
- package/dist/detect/patterns/html-clients.json +27 -0
- package/dist/detect/patterns/quoted-headers.json +7 -0
- package/dist/detect/patterns/reply-headers.json +410 -0
- package/dist/detect/patterns.d.ts +2 -0
- package/dist/detect/patterns.d.ts.map +1 -1
- package/dist/detect/patterns.js +3 -0
- package/dist/detect/patterns.js.map +1 -1
- package/dist/detect/reply-header.d.ts +7 -0
- package/dist/detect/reply-header.d.ts.map +1 -1
- package/dist/detect/reply-header.js +51 -4
- package/dist/detect/reply-header.js.map +1 -1
- package/dist/detect/subject.d.ts +16 -0
- package/dist/detect/subject.d.ts.map +1 -0
- package/dist/detect/subject.js +22 -0
- package/dist/detect/subject.js.map +1 -0
- package/dist/eval/metrics.d.ts +7 -0
- package/dist/eval/metrics.d.ts.map +1 -1
- package/dist/eval/metrics.js +1 -1
- package/dist/eval/metrics.js.map +1 -1
- package/dist/eval/pattern-audit.d.ts +56 -0
- package/dist/eval/pattern-audit.d.ts.map +1 -0
- package/dist/eval/pattern-audit.js +111 -0
- package/dist/eval/pattern-audit.js.map +1 -0
- package/dist/eval/reference.d.ts +51 -0
- package/dist/eval/reference.d.ts.map +1 -0
- package/dist/eval/reference.js +96 -0
- package/dist/eval/reference.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mime/flowed.d.ts +14 -0
- package/dist/mime/flowed.d.ts.map +1 -0
- package/dist/mime/flowed.js +71 -0
- package/dist/mime/flowed.js.map +1 -0
- package/dist/mime/parse.js +2 -5
- package/dist/mime/parse.js.map +1 -1
- package/dist/segment/features.d.ts.map +1 -1
- package/dist/segment/features.js +2 -1
- package/dist/segment/features.js.map +1 -1
- package/dist/segment/html-blocks.d.ts.map +1 -1
- package/dist/segment/html-blocks.js +33 -9
- package/dist/segment/html-blocks.js.map +1 -1
- package/dist/segment/text-blocks.d.ts.map +1 -1
- package/dist/segment/text-blocks.js +18 -3
- package/dist/segment/text-blocks.js.map +1 -1
- package/dist/segment/types.d.ts +7 -0
- package/dist/segment/types.d.ts.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/package.json +5 -5
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
"flags": "i",
|
|
17
17
|
"pattern": "^\\s*On\\s+.{3,200}?(\\b\\d{4}\\b|\\b\\d{1,2}:\\d{2}\\b).{0,80}[,:]?\\s*$"
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
"id": "on_weekday_wrote_nospace_en",
|
|
21
|
+
"lang": "en",
|
|
22
|
+
"comment": "Lark Mail drops the space after On: 'OnTue, Sep 29, 2026, 12:15 <address> wrote:'. Anchored on a weekday name so ordinary words starting with On do not match.",
|
|
23
|
+
"strength": 0.95,
|
|
24
|
+
"flags": "i",
|
|
25
|
+
"pattern": "^\\s*On(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\\w*,.{3,200}?\\bwrote\\s*:\\s*$"
|
|
26
|
+
},
|
|
19
27
|
{
|
|
20
28
|
"id": "x_wrote_trailing",
|
|
21
29
|
"lang": "en",
|
|
@@ -117,6 +125,318 @@
|
|
|
117
125
|
"flags": "i",
|
|
118
126
|
"pattern": "^\\s*\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}.{0,120}<[^>]+@[^>]+>.{0,40}:\\s*$"
|
|
119
127
|
},
|
|
128
|
+
{
|
|
129
|
+
"id": "dmy_time_addr_colon",
|
|
130
|
+
"lang": "*",
|
|
131
|
+
"comment": "Yandex Mail's attribution: day first date, time, name and address, then a colon and no verb: '29.09.2026, 11:30, \"Marta Kask\" <address>:', or with the address in parentheses in a forward.",
|
|
132
|
+
"strength": 0.95,
|
|
133
|
+
"flags": "i",
|
|
134
|
+
"pattern": "^\\s*\\d{1,2}\\.\\d{1,2}\\.\\d{4},\\s*\\d{1,2}:\\d{2},\\s*.{0,120}?(<[^>\\s]+@[^>\\s]+>|\\([^)\\s]+@[^)\\s]+\\))\\s*:\\s*$"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"id": "el_x_va_escriure_ca",
|
|
138
|
+
"lang": "ca",
|
|
139
|
+
"comment": "Catalan, as Roundcube writes it: 'El 2026-09-29 13:55, Marta Kask va escriure:'.",
|
|
140
|
+
"strength": 0.95,
|
|
141
|
+
"flags": "i",
|
|
142
|
+
"pattern": "^\\s*El\\s+.{3,200}?va\\s+escriure\\s*:\\s*$"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "o_x_escribiu_gl",
|
|
146
|
+
"lang": "gl",
|
|
147
|
+
"comment": "Galician, as Roundcube writes it: 'O 2026-09-29 11:46, Marta Kask escribiu:'.",
|
|
148
|
+
"strength": 0.95,
|
|
149
|
+
"flags": "i",
|
|
150
|
+
"pattern": "^\\s*O\\s+.{3,200}?escribiu\\s*:\\s*$"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "me_x_shkroi_sq",
|
|
154
|
+
"lang": "sq",
|
|
155
|
+
"comment": "Albanian, as Roundcube writes it: 'Më 2026-09-29 12:36, Marta Kask shkroi:'.",
|
|
156
|
+
"strength": 0.95,
|
|
157
|
+
"flags": "i",
|
|
158
|
+
"pattern": "^\\s*Më\\s+.{3,200}?shkroi\\s*:\\s*$"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "na_x_napisa_bg_sr_mk",
|
|
162
|
+
"lang": "*",
|
|
163
|
+
"comment": "Bulgarian, Serbian and Macedonian, as Roundcube writes them: 'На 2026-09-29 12:28, Marta Kask написа:', 'Дана ... написа:'. Roundcube's Macedonian template ships unfilled ('На $дата,$праќач напиша:') and is still an attribution.",
|
|
164
|
+
"strength": 0.95,
|
|
165
|
+
"flags": "i",
|
|
166
|
+
"pattern": "^\\s*(На|Дана)\\s+.{3,200}?(написа|напиша)\\s*:\\s*$"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "dana_x_je_napisao_hr",
|
|
170
|
+
"lang": "hr",
|
|
171
|
+
"comment": "Croatian, as Roundcube writes it: 'Dana 2026-09-29 12:31, Marta Kask je napisao(la):'.",
|
|
172
|
+
"strength": 0.95,
|
|
173
|
+
"flags": "i",
|
|
174
|
+
"pattern": "^\\s*Dana\\s+.{3,200}?je\\s+napisao(\\(la\\)|la)?\\s*:\\s*$"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "x_napisa_dated_cyrillic",
|
|
178
|
+
"lang": "*",
|
|
179
|
+
"comment": "Ukrainian, Belarusian, Bulgarian and Serbian without an opener, verb followed by an optional date: 'Marta Kask напісаў 2026-09-29 12:28:', '... \"Marta Kask\" <...> написа:'. Requires a clock time on the line, since the bare verb also occurs in prose.",
|
|
180
|
+
"strength": 0.9,
|
|
181
|
+
"flags": "i",
|
|
182
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(написав|напісаў|написал[аи]?|написа|напиша).{0,60}?\\s*[::៖]{1,2}\\s*$"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"id": "x_egrapse_el",
|
|
186
|
+
"lang": "el",
|
|
187
|
+
"comment": "Greek: 'Στις 2026-09-29 11:47, Marta Kask έγραψε:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
188
|
+
"strength": 0.9,
|
|
189
|
+
"flags": "i",
|
|
190
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?έγραψε\\s*[::៖]{1,2}\\s*$"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "x_idatzi_eu",
|
|
194
|
+
"lang": "eu",
|
|
195
|
+
"comment": "Basque: '2026-09-29 11:43 egunean, Marta Kask-(e)k idatzi du:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
196
|
+
"strength": 0.9,
|
|
197
|
+
"flags": "i",
|
|
198
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(idatzi\\s+du|idatzia)\\s*[::៖]{1,2}\\s*$"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "x_kirjoitti_dated_fi",
|
|
202
|
+
"lang": "fi",
|
|
203
|
+
"comment": "Finnish with the date after the verb: 'Marta Kask kirjoitti 2026-09-29 12:15:'. The older x_kirjoitti_fi only accepts the verb right before the colon. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
204
|
+
"strength": 0.9,
|
|
205
|
+
"flags": "i",
|
|
206
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?kirjoitti.{0,60}?\\s*[::៖]{1,2}\\s*$"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"id": "x_schreef_dated_nl",
|
|
210
|
+
"lang": "nl",
|
|
211
|
+
"comment": "Dutch without the Op opener: 'Marta Kask schreef op 2026-09-29 11:46:'. Requires a clock time on the line, since 'schreef' is ordinary prose.",
|
|
212
|
+
"strength": 0.9,
|
|
213
|
+
"flags": "i",
|
|
214
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?schreef.{0,80}?\\s*[::៖]{1,2}\\s*$"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"id": "x_skrev_dated_da_nb",
|
|
218
|
+
"lang": "*",
|
|
219
|
+
"comment": "Danish and Norwegian without the Den opener: '\"Marta Kask\" <...> skrev 29. september 2026 kl. 10:10:', including SnappyMail's doubled colon. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
220
|
+
"strength": 0.9,
|
|
221
|
+
"flags": "i",
|
|
222
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?skrev.{0,80}?\\s*[::៖]{1,2}\\s*$"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"id": "x_skrifadi_is",
|
|
226
|
+
"lang": "is",
|
|
227
|
+
"comment": "Icelandic: 'September 29, 2026 at 10:06 AM, skrifaði \"Marta Kask\" <...>:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
228
|
+
"strength": 0.9,
|
|
229
|
+
"flags": "i",
|
|
230
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?skrifaði.{0,80}?\\s*[::៖]{1,2}\\s*$"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"id": "x_irta_hu",
|
|
234
|
+
"lang": "hu",
|
|
235
|
+
"comment": "Hungarian: '2026-09-29 11:47 időpontban Marta Kask ezt írta:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
236
|
+
"strength": 0.9,
|
|
237
|
+
"flags": "i",
|
|
238
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(ezt\\s+)?írta\\s*[::៖]{1,2}\\s*$"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "x_menulis_id",
|
|
242
|
+
"lang": "id",
|
|
243
|
+
"comment": "Indonesian: 'Pada 2026-09-29 11:47, Marta Kask menulis:', SnappyMail '... ditulis:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
244
|
+
"strength": 0.9,
|
|
245
|
+
"flags": "i",
|
|
246
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(menulis|ditulis)\\s*[::៖]{1,2}\\s*$"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "x_kakimashita_ja",
|
|
250
|
+
"lang": "ja",
|
|
251
|
+
"comment": "Japanese: '2026-09-29 12:33 に Marta Kask さんは書きました:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
252
|
+
"strength": 0.9,
|
|
253
|
+
"flags": "i",
|
|
254
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(書きました|書き込みました)\\s*[::៖]{1,2}\\s*$"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"id": "x_dawera_ka",
|
|
258
|
+
"lang": "ka",
|
|
259
|
+
"comment": "Georgian: '2026-09-29 11:46-ში, Marta Kask დაწერა:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
260
|
+
"strength": 0.9,
|
|
261
|
+
"flags": "i",
|
|
262
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?დაწერა\\s*[::៖]{1,2}\\s*$"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"id": "x_sarser_km",
|
|
266
|
+
"lang": "km",
|
|
267
|
+
"comment": "Khmer, which ends the line with its own colon sign: 'កាលពីថ្ងៃ 2026-09-29 12:33, Marta Kask បានសរសេរ៖'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
268
|
+
"strength": 0.9,
|
|
269
|
+
"flags": "i",
|
|
270
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?បាន[\\s]*សរសេរ\\s*[::៖]{1,2}\\s*$"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"id": "x_nivisi_ku",
|
|
274
|
+
"lang": "ku",
|
|
275
|
+
"comment": "Kurdish: 'Di 2026-09-29 11:48 de, şandyar nivîsî:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
276
|
+
"strength": 0.9,
|
|
277
|
+
"flags": "i",
|
|
278
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?nivîsî\\s*[::៖]{1,2}\\s*$"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"id": "x_rakstija_lv",
|
|
282
|
+
"lang": "lv",
|
|
283
|
+
"comment": "Latvian: 'Marta Kask @ 2026-09-29 12:35 rakstīja:', SnappyMail writes it without the macron. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
284
|
+
"strength": 0.9,
|
|
285
|
+
"flags": "i",
|
|
286
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?rakst[īi]ja\\s*[::៖]{1,2}\\s*$"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "x_parase_lt",
|
|
290
|
+
"lang": "lt",
|
|
291
|
+
"comment": "Lithuanian: '2026 m. rugsėjo 29 d. 10:08, \"Marta Kask\" <...> parašė:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
292
|
+
"strength": 0.9,
|
|
293
|
+
"flags": "i",
|
|
294
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?parašė\\s*[::៖]{1,2}\\s*$"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "x_napisal_dated_pl",
|
|
298
|
+
"lang": "pl",
|
|
299
|
+
"comment": "Polish without the W dniu opener: '29 września 2026 10:11, \"Marta Kask\" <...> napisał/-a/:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
300
|
+
"strength": 0.9,
|
|
301
|
+
"flags": "i",
|
|
302
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?napisał\\S*\\s*[::៖]{1,2}\\s*$"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"id": "x_napisal_sl",
|
|
306
|
+
"lang": "sl",
|
|
307
|
+
"comment": "Slovenian: '29. september 2026 ob 10:15, \"Marta Kask\" <...> je napisal/-a:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
308
|
+
"strength": 0.9,
|
|
309
|
+
"flags": "i",
|
|
310
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(je\\s+)?napisal\\S*\\s*[::៖]{1,2}\\s*$"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"id": "x_je_napisal_nocolon_sl",
|
|
314
|
+
"lang": "sl",
|
|
315
|
+
"comment": "Slovenian as Roundcube writes it, with no colon: '2026-09-29 11:50, je Marta Kask napisal'. Must start with the date, since without a colon the shape is otherwise ordinary prose.",
|
|
316
|
+
"strength": 0.9,
|
|
317
|
+
"flags": "i",
|
|
318
|
+
"pattern": "^\\s*\\d{4}-\\d{2}-\\d{2}[ T]\\d{1,2}:\\d{2},\\s+je\\s+.{1,120}?\\s+napisal(a|\\/-a)?\\s*:?\\s*$"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"id": "x_viet_vi",
|
|
322
|
+
"lang": "vi",
|
|
323
|
+
"comment": "Vietnamese: 'Vào ngày 2026-09-29 12:27, Marta Kask viết:', SnappyMail '... đã viết là:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
324
|
+
"strength": 0.9,
|
|
325
|
+
"flags": "i",
|
|
326
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(viết|đã\\s+viết\\s+là)\\s*[::៖]{1,2}\\s*$"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"id": "x_escribio_dated_es",
|
|
330
|
+
"lang": "es",
|
|
331
|
+
"comment": "Spanish without the El opener: '29 de septiembre de 2026 a las 10:01, \"Marta Kask\" <...> escribió:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
332
|
+
"strength": 0.9,
|
|
333
|
+
"flags": "i",
|
|
334
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?escribi[oó]\\s*[::៖]{1,2}\\s*$"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"id": "x_escreveu_dated_pt",
|
|
338
|
+
"lang": "pt",
|
|
339
|
+
"comment": "Portuguese without the Em opener: '29 de setembro de 2026 às 10:12, \"Marta Kask\" <...> escreveu:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
340
|
+
"strength": 0.9,
|
|
341
|
+
"flags": "i",
|
|
342
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?escreveu\\s*[::៖]{1,2}\\s*$"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"id": "x_a_ecrit_dated_fr",
|
|
346
|
+
"lang": "fr",
|
|
347
|
+
"comment": "French without the Le opener: '29 septembre 2026 à 10:04 \"Marta Kask\" <...> a écrit:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
348
|
+
"strength": 0.9,
|
|
349
|
+
"flags": "i",
|
|
350
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?a\\s+écrit\\s*[::៖]{1,2}\\s*$"
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"id": "x_scrive_dated_it",
|
|
354
|
+
"lang": "it",
|
|
355
|
+
"comment": "Italian without the Il opener: '29 settembre 2026 alle ore 10:07, \"Marta Kask\" <...> scrive:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
356
|
+
"strength": 0.9,
|
|
357
|
+
"flags": "i",
|
|
358
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(scrive|ha\\s+scritto)\\s*[::៖]{1,2}\\s*$"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"id": "x_a_scris_ro",
|
|
362
|
+
"lang": "ro",
|
|
363
|
+
"comment": "Romanian: '29 septembrie 2026 la 10:13, \"Marta Kask\" <...> a scris:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
364
|
+
"strength": 0.9,
|
|
365
|
+
"flags": "i",
|
|
366
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?a\\s+scris\\s*[::៖]{1,2}\\s*$"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"id": "x_yazdi_tr",
|
|
370
|
+
"lang": "tr",
|
|
371
|
+
"comment": "Turkish: '29 Eylül 2026 10:16, \"Marta Kask\" <...> yazdı:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
372
|
+
"strength": 0.9,
|
|
373
|
+
"flags": "i",
|
|
374
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?yazdı\\s*[::៖]{1,2}\\s*$"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"id": "x_bonaen_ko",
|
|
378
|
+
"lang": "ko",
|
|
379
|
+
"comment": "Korean: '2026년 9월 29일 오전 10:08에 \"Marta Kask\" <...>이(가) 보낸 메시지:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
380
|
+
"strength": 0.9,
|
|
381
|
+
"flags": "i",
|
|
382
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(보낸\\s*메시지|작성)\\s*[::៖]{1,2}\\s*$"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"id": "x_xiedao_zh",
|
|
386
|
+
"lang": "zh",
|
|
387
|
+
"comment": "Chinese, simplified and traditional, with an ASCII or full width colon: '2026年9月29日 10:18, \"Marta Kask\" <...> 写到:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
388
|
+
"strength": 0.9,
|
|
389
|
+
"flags": "i",
|
|
390
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(写到|寫到|写道|寫道)\\s*[::៖]{1,2}\\s*$"
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"id": "x_nevesht_fa",
|
|
394
|
+
"lang": "fa",
|
|
395
|
+
"comment": "Persian: '۷ مهر ۱۴۰۵ ساعت ۱۰:۰۳, \"Marta Kask\" <...> نوشت:'. The digit lookahead accepts Persian and Arabic-Indic digits. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
396
|
+
"strength": 0.9,
|
|
397
|
+
"flags": "i",
|
|
398
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?نوشت\\s*[::៖]{1,2}\\s*$"
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"id": "katab_x_ar",
|
|
402
|
+
"lang": "ar",
|
|
403
|
+
"comment": "Arabic, as Roundcube writes it, verb first: 'كتب Marta Kask في 2026-09-29 11:43:'.",
|
|
404
|
+
"strength": 0.9,
|
|
405
|
+
"flags": "i",
|
|
406
|
+
"pattern": "^\\s*كتب\\s+.{1,160}?[0-9٠-٩].{0,60}:\\s*$"
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"id": "katav_x_he",
|
|
410
|
+
"lang": "he",
|
|
411
|
+
"comment": "Hebrew, as Roundcube writes it, in logical order with the colon first: ':כתב Marta Kask, 2026-09-29 12:30 בתאריך'.",
|
|
412
|
+
"strength": 0.9,
|
|
413
|
+
"flags": "i",
|
|
414
|
+
"pattern": "^\\s*:\\s*כתב\\s+.{1,160}?\\d{4}.{0,60}$"
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"id": "x_skrivet_gant_br",
|
|
418
|
+
"lang": "br",
|
|
419
|
+
"comment": "Breton: \"D'an/ar 2026-09-29 13:54 eo bet skrivet gant Marta Kask:\". Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
420
|
+
"strength": 0.9,
|
|
421
|
+
"flags": "i",
|
|
422
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?skrivet\\s+gant.{1,120}?\\s*[::៖]{1,2}\\s*$"
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"id": "dated_addr_said_nocolon",
|
|
426
|
+
"lang": "*",
|
|
427
|
+
"comment": "Cypht's attribution: date, name, address, then a verb with no colon, in many languages ('said', 'sagte', 'a dit', 'mengatakan', 'napisał(a)', ...): 'Am Tue, 29 Sep 2026 08:09:34 +0000Marta Kask marta.kask@example.com sagte'. Requires both a date and an address on the line, since without a colon the verb alone is prose.",
|
|
428
|
+
"strength": 0.9,
|
|
429
|
+
"flags": "i",
|
|
430
|
+
"pattern": "^\\s*(?=.*\\d{1,2}:\\d{2})(?=[^@]*[^\\s@]@[^\\s@]+\\.[a-z]{2,}).{10,250}?\\s(said|sagte|ütles|ha scritto|disse|bildirib|گفت|zei|a scris|kirjutas|schreef|escribió|a écrit|a dit|mengatakan|napisał\\(a\\)|napisał)\\s*:?\\s*$"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"id": "op_x_skreau_fy",
|
|
434
|
+
"lang": "fy",
|
|
435
|
+
"comment": "Frisian, as Roundcube writes it: 'Op 2026-09-29 13:56, skreau Marta Kask:'.",
|
|
436
|
+
"strength": 0.95,
|
|
437
|
+
"flags": "i",
|
|
438
|
+
"pattern": "^\\s*Op\\s+.{3,200}?skreau\\s+.{1,120}:\\s*$"
|
|
439
|
+
},
|
|
120
440
|
{
|
|
121
441
|
"id": "original_message_separator",
|
|
122
442
|
"lang": "*",
|
|
@@ -124,6 +444,54 @@
|
|
|
124
444
|
"flags": "i",
|
|
125
445
|
"pattern": "^\\s*-{2,}\\s*(Original Message|Originalnachricht|Message d'origine|Mensaje original|Messaggio originale|Oorspronkelijk bericht|Algne teade|Исходное сообщение)\\s*-{2,}\\s*$"
|
|
126
446
|
},
|
|
447
|
+
{
|
|
448
|
+
"id": "x_wrote_dated_celtic_nordic_turkic",
|
|
449
|
+
"lang": "*",
|
|
450
|
+
"comment": "Welsh 'Ar 2026-09-29 14:04, ysgrifennodd Marta Kask:', Irish 'Ar an ..., scríobh Marta Kask:', Nynorsk 'Den ... skreiv Marta Kask:', Turkish '..., Marta Kask yazmış:', Uzbek 'Marta Kask yozgan kun 2026-09-29 14:04:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
451
|
+
"strength": 0.9,
|
|
452
|
+
"flags": "i",
|
|
453
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(ysgrifennodd|scríobh|skreiv|yazmış|yozgan).{0,80}?\\s*:\\s*$"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"id": "x_napsal_dated_cs_sk_az",
|
|
457
|
+
"lang": "*",
|
|
458
|
+
"comment": "Czech 'Dne 2026-09-29 14:24, Marta Kask napsal:', Slovak 'Dňa ... Marta Kask napísal(a):', Azerbaijani '2026-09-29 14:24 tarixdə, Marta Kask yazıb:'. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
459
|
+
"strength": 0.9,
|
|
460
|
+
"flags": "i",
|
|
461
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(napsal(a)?|napísal(\\(a\\)|a)?|yazıb)\\s*:\\s*$"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"id": "x_wrote_dated_more",
|
|
465
|
+
"lang": "*",
|
|
466
|
+
"comment": "More Roundcube translations: Bosnian 'je napisao/la:', Esperanto 'skribis:', Estonian '2026-09-29 14:26 kirjutas Marta Kask:', Persian 'نوشته است:', Faroese 'skrivaði Marta Kask:', Interlingua 'ha scribite:', Korean '쓴 내용:', Luxembourgish 'schreift:', Lithuanian 'rašė:', Marathi 'लिहीले :', Armenian 'գրեց.' (ending in a full stop), and Uyghur 'يازدى:', whose template ships unfilled. Requires a clock time on the line, which attribution lines carry and prose rarely does.",
|
|
467
|
+
"strength": 0.9,
|
|
468
|
+
"flags": "i",
|
|
469
|
+
"pattern": "^\\s*(?=.*[0-9٠-٩۰-۹]{1,2}[:.][0-9٠-٩۰-۹]{2}).{3,200}?(je\\s+napisao\\/la|skribis|kirjutas|نوشته\\s+است|skrivaði|ha\\s+scribite|쓴\\s*내용|schreift|rašė|लिहीले|գրեց|يازدى).{0,80}?\\s*[:.։]\\s*$"
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
"id": "pada_x_menulis_nocolon_ms",
|
|
473
|
+
"lang": "ms",
|
|
474
|
+
"comment": "Malay as Roundcube writes it, with no colon: 'Pada 2026-09-29 14:27, Marta Kask menulis'. Anchored on the opener and a timestamp, since without a colon the verb alone is prose.",
|
|
475
|
+
"strength": 0.9,
|
|
476
|
+
"flags": "i",
|
|
477
|
+
"pattern": "^\\s*Pada\\s+\\d{4}-\\d{2}-\\d{2}[ T]\\d{1,2}:\\d{2},\\s+.{1,120}?\\s+menulis\\s*:?\\s*$"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
"id": "roundcube_unfilled_template",
|
|
481
|
+
"lang": "*",
|
|
482
|
+
"comment": "Roundcube's Uyghur attribution template ships with its placeholders unfilled: 'date$ دا، sender$ يازدى:'. The literal placeholders cannot occur in prose.",
|
|
483
|
+
"strength": 0.95,
|
|
484
|
+
"flags": "",
|
|
485
|
+
"pattern": "^\\s*date\\$.{0,40}sender\\$.{0,60}:\\s*$"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"id": "original_message_separator_localised",
|
|
489
|
+
"lang": "*",
|
|
490
|
+
"comment": "The 'Original Message' separator as Roundcube and SnappyMail translate it, collected from test/fixtures/clients. Roundcube uses it to introduce forwards, which the classifier turns into forwarded content when the subject marks a forward.",
|
|
491
|
+
"strength": 0.97,
|
|
492
|
+
"flags": "i",
|
|
493
|
+
"pattern": "^\\s*-{2,}\\s*(원본 메시지|ಮೂಲ ಸಂದೇಶ|הודעה מקורית|Αρχικό μήνυμα|الرسالة الأساسية|Оригинална порака|ორიგინალი შეტყობინება|Оригінальне повідомлення|Арыгінальнае паведамленне|原始邮件|元のメッセージ|Alkuperäinen viesti|An teachtaireacht bhunaidh|Eredeti üzenet|Izvorna poruka|Jatorrizko mezua|Kemennadenn orin|Missatge Original|Neges Wreiddiol|Oarspronklik Berjocht|Oorspronkelijke bericht|Opprinnelig melding|Originalus laiškas|Orijinal məktub|Peyama orîjînal|Sākotnējā vēstule|Upprunaleg skilaboð|Mesajul original|खरा संदेश|Оригинално писмо|原始郵件|សារដើម|Mensaxe orixinal|Originalna poruka|Původní zpráva|Original besked|Originala mesaĝo|Esialgne kiri|پیام اصلی|Uppruna boð|Courriel original|Առաջին հաղորդագրությունը|Message original|Pesan Asli|Original-Message|യഥാര്ഥ സന്ദേശം|യഥാര്ഥ സന്ദേശം|Mesej Asal|Orgineel bericht|Opphavleg melding|Wiadomość oryginalna|Mensagem original|Mesaj original|Pôvodná správa|Izvorno sporočilo|Mesazhi Origjinal|Оригинална порука|Ursprungligt meddelande|ข้อความเดิม|Özgün ileti|ئەسلىي خەت|Asl xat|Thư gốc)\\s*-{2,}\\s*$"
|
|
494
|
+
},
|
|
127
495
|
{
|
|
128
496
|
"id": "reply_message_separator",
|
|
129
497
|
"lang": "*",
|
|
@@ -131,5 +499,47 @@
|
|
|
131
499
|
"flags": "i",
|
|
132
500
|
"pattern": "^\\s*-{2,}\\s*(Reply Message|Antwort|Svar)\\s*-{2,}\\s*$"
|
|
133
501
|
}
|
|
502
|
+
],
|
|
503
|
+
"inlineAnnouncements": [
|
|
504
|
+
{
|
|
505
|
+
"id": "answers_inline_en",
|
|
506
|
+
"lang": "en",
|
|
507
|
+
"strength": 0.8,
|
|
508
|
+
"flags": "i",
|
|
509
|
+
"comment": "'my answers are inline', 'replies are inline', 'comments inline'. The sender says the reply is written into the quoted text.",
|
|
510
|
+
"pattern": "\\b(answers|comments|replies|responses)\\s+(are\\s+)?inline\\b"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"id": "answers_below_en",
|
|
514
|
+
"lang": "en",
|
|
515
|
+
"strength": 0.8,
|
|
516
|
+
"flags": "i",
|
|
517
|
+
"comment": "'My comments below', 'Some replies below', 'see my comments below'. Requires a possessive or quantifier, because 'the answers below' in prose points at the sender's own text.",
|
|
518
|
+
"pattern": "\\b(my|some|see my|please see my)\\s+(answers|comments|replies|responses|notes)\\s+(are\\s+)?below\\b"
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
"id": "answers_in_your_letter_en",
|
|
522
|
+
"lang": "en",
|
|
523
|
+
"strength": 0.8,
|
|
524
|
+
"flags": "i",
|
|
525
|
+
"comment": "'Answers are in your letter', 'my comments are in your email'.",
|
|
526
|
+
"pattern": "\\b(answers|comments|replies|responses)\\s+are\\s+in\\s+your\\s+(letter|mail|e-?mail|message)\\b"
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
"id": "answers_in_letter_et",
|
|
530
|
+
"lang": "et",
|
|
531
|
+
"strength": 0.8,
|
|
532
|
+
"flags": "i",
|
|
533
|
+
"comment": "'Vastused on kirja sees' (the answers are inside the letter), 'panin oma mõtted sinu maili sisse' (I put my thoughts into your mail), with or without 'punasega' (in red).",
|
|
534
|
+
"pattern": "\\b(vastused|mõtted|kommentaarid)\\b[^.\\n]{0,30}\\b(kirja|maili)\\s+(sees|sisse)\\b"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"id": "my_answers_et",
|
|
538
|
+
"lang": "et",
|
|
539
|
+
"strength": 0.8,
|
|
540
|
+
"flags": "i",
|
|
541
|
+
"comment": "'minu vastused.' as a short lead-in to answers typed into the quoted questions. Requires sentence end, because 'minu vastused Sinu küsimustele: 1)' introduces answers the sender wrote out in full.",
|
|
542
|
+
"pattern": "\\bminu\\s+vastused\\s*[.!]"
|
|
543
|
+
}
|
|
134
544
|
]
|
|
135
545
|
}
|
|
@@ -29,6 +29,8 @@ export interface PatternSets {
|
|
|
29
29
|
replyHeaders: LinePattern[];
|
|
30
30
|
forwardHeaders: LinePattern[];
|
|
31
31
|
quotedHeaderFields: LinePattern[];
|
|
32
|
+
/** Phrases announcing that the reply is written into the quoted text. Matched anywhere in a line. */
|
|
33
|
+
inlineAnnouncements: LinePattern[];
|
|
32
34
|
signatureDelimiters: LinePattern[];
|
|
33
35
|
signaturePhrases: LinePattern[];
|
|
34
36
|
disclaimerPhrases: LinePattern[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/detect/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,CAAC;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAClC,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACnC,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAcD;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,6BAA6B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,IAAW;CAK1D;AA+GD,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/detect/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,CAAC;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAClC,qGAAqG;IACrG,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACnC,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACnC,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAcD;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,6BAA6B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,IAAW;CAK1D;AA+GD,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAWvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ5E;AAsDD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAErE;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC1B,iGAAiG;IACjG,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,aAAa,CAG7C;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
package/dist/detect/patterns.js
CHANGED
|
@@ -148,6 +148,7 @@ function countPatterns(sets) {
|
|
|
148
148
|
return (sets.replyHeaders.length +
|
|
149
149
|
sets.forwardHeaders.length +
|
|
150
150
|
sets.quotedHeaderFields.length +
|
|
151
|
+
sets.inlineAnnouncements.length +
|
|
151
152
|
sets.signatureDelimiters.length +
|
|
152
153
|
sets.signaturePhrases.length +
|
|
153
154
|
sets.disclaimerPhrases.length +
|
|
@@ -199,6 +200,8 @@ function loadUncached(dir) {
|
|
|
199
200
|
replyHeaders: compileLinePatterns(files['reply-headers.json'].patterns, invalid),
|
|
200
201
|
forwardHeaders: compileLinePatterns(files['forward-headers.json'].patterns, invalid),
|
|
201
202
|
quotedHeaderFields: compileLinePatterns(files['quoted-headers.json'].fields, invalid),
|
|
203
|
+
// Optional, so an override directory written before the set existed still loads.
|
|
204
|
+
inlineAnnouncements: compileLinePatterns(files['reply-headers.json'].inlineAnnouncements, invalid),
|
|
202
205
|
signatureDelimiters: compileLinePatterns(signatures.delimiters, invalid),
|
|
203
206
|
signaturePhrases: compileLinePatterns(signatures.phrases, invalid),
|
|
204
207
|
disclaimerPhrases: compileLinePatterns(files['disclaimers.json'].phrases, invalid),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/detect/patterns.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/detect/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,13 @@ export interface ReplyHeaderMatch {
|
|
|
27
27
|
* one is almost always a paragraph that happens to end in "wrote:".
|
|
28
28
|
*/
|
|
29
29
|
export declare function detectAttributionLine(line: string): Evidence | null;
|
|
30
|
+
/**
|
|
31
|
+
* A sentence announcing that the reply is written into the quoted text: "my answers are inline",
|
|
32
|
+
* "Answers are in your letter". Evidence only; the rules decide what it changes, and only for
|
|
33
|
+
* authored text above the history. Lines are bounded like attribution lines so a hostile
|
|
34
|
+
* single-line body stays cheap.
|
|
35
|
+
*/
|
|
36
|
+
export declare function detectInlineAnnouncement(lines: readonly string[]): Evidence | null;
|
|
30
37
|
/**
|
|
31
38
|
* Detect an embedded header block starting at or after `from`.
|
|
32
39
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-header.d.ts","sourceRoot":"","sources":["../../src/detect/reply-header.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAkCjD,MAAM,WAAW,gBAAgB;IAC7B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"reply-header.d.ts","sourceRoot":"","sources":["../../src/detect/reply-header.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAkCjD,MAAM,WAAW,gBAAgB;IAC7B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CA2BnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG,IAAI,CAYlF;AAoBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,SAAI,GAAG,gBAAgB,GAAG,IAAI,CAkFnG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CASjE"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.detectAttributionLine = detectAttributionLine;
|
|
18
|
+
exports.detectInlineAnnouncement = detectInlineAnnouncement;
|
|
18
19
|
exports.detectQuotedHeaderBlock = detectQuotedHeaderBlock;
|
|
19
20
|
exports.detectRuleSeparator = detectRuleSeparator;
|
|
20
21
|
const patterns_1 = require("./patterns");
|
|
@@ -51,7 +52,18 @@ function valueOf(line) {
|
|
|
51
52
|
* one is almost always a paragraph that happens to end in "wrote:".
|
|
52
53
|
*/
|
|
53
54
|
function detectAttributionLine(line) {
|
|
54
|
-
|
|
55
|
+
// Clients that derive plain text from HTML write links out as "mailto:" URLs, and SnappyMail
|
|
56
|
+
// puts one inside every attribution, fully URL encoded: '"Marta Kask" <marta@example.com
|
|
57
|
+
// mailto:marta@example.com?to=%22Marta...%3E > schrieb:'. The URL says nothing about the line's
|
|
58
|
+
// shape but pushes it past the length limits, so it is dropped before matching. The raw line is
|
|
59
|
+
// bounded first so the replace stays cheap on hostile input.
|
|
60
|
+
if (line.length > 2000) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
// Only lines that contain a URL pay for the replace, and it has no leading whitespace class:
|
|
64
|
+
// "\s*" before the literal was retried at every position of a long space run, quadratic in
|
|
65
|
+
// its length (1 MB of such lines blocked for over a second).
|
|
66
|
+
const trimmed = (/mailto:/i.test(line) ? line.replace(/ ?\bmailto:\S+/gi, '') : line).trim();
|
|
55
67
|
if (!trimmed || trimmed.length > 300) {
|
|
56
68
|
return null;
|
|
57
69
|
}
|
|
@@ -67,11 +79,40 @@ function detectAttributionLine(line) {
|
|
|
67
79
|
}
|
|
68
80
|
return best;
|
|
69
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* A sentence announcing that the reply is written into the quoted text: "my answers are inline",
|
|
84
|
+
* "Answers are in your letter". Evidence only; the rules decide what it changes, and only for
|
|
85
|
+
* authored text above the history. Lines are bounded like attribution lines so a hostile
|
|
86
|
+
* single-line body stays cheap.
|
|
87
|
+
*/
|
|
88
|
+
function detectInlineAnnouncement(lines) {
|
|
89
|
+
const patterns = (0, patterns_1.loadPatterns)().inlineAnnouncements;
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
if (line.length > 2000) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const pattern = patterns.find(candidate => candidate.regex.test(line));
|
|
95
|
+
if (pattern) {
|
|
96
|
+
return { id: `announce:${pattern.id}`, kind: 'hint', strength: pattern.strength };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
70
101
|
/** Is this run of header-shaped lines convincing enough to anchor a region? */
|
|
71
102
|
function isStrong(roles) {
|
|
72
103
|
// A convincing block names the originator plus at least a date or a subject, so that a
|
|
73
|
-
// pasted "To: ... Cc: ..." address list on its own does not qualify.
|
|
74
|
-
|
|
104
|
+
// pasted "To: ... Cc: ..." address list on its own does not qualify. A transport field means a
|
|
105
|
+
// raw header dump, which clients never produce in a reply, so it disqualifies the chain.
|
|
106
|
+
return !roles.has('transport') && roles.size >= MIN_FIELDS && roles.has('originator') && (roles.has('date') || roles.has('subject'));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A field line that is really source code or prose: a lowercase field name ("from:" in a JavaScript
|
|
110
|
+
* object literal) or a value that ends in a comma. Client header blocks capitalise the field name
|
|
111
|
+
* in every language the patterns cover and never end a value with a comma. Measured on the corpus:
|
|
112
|
+
* a pasted nodemailer example hid the console output the sender asked about.
|
|
113
|
+
*/
|
|
114
|
+
function looksLikeCode(line, value) {
|
|
115
|
+
return /^[a-z]/.test(line) || value.endsWith(',');
|
|
75
116
|
}
|
|
76
117
|
/**
|
|
77
118
|
* Detect an embedded header block starting at or after `from`.
|
|
@@ -154,7 +195,13 @@ function detectQuotedHeaderBlock(lines, from = 0) {
|
|
|
154
195
|
// Only fields whose value looks like a real header value count towards the threshold.
|
|
155
196
|
// The line still continues the block either way, so a "Subject:" with unusual content
|
|
156
197
|
// does not truncate an otherwise convincing header block.
|
|
157
|
-
if (role === '
|
|
198
|
+
if (role === 'transport') {
|
|
199
|
+
roles.add(role);
|
|
200
|
+
}
|
|
201
|
+
else if (looksLikeCode(line, value)) {
|
|
202
|
+
// Continues the chain like any field line, but earns no role.
|
|
203
|
+
}
|
|
204
|
+
else if (role === 'originator' ? ORIGINATOR_VALUE.test(value) : role === 'date' ? DATE_VALUE.test(value) : true) {
|
|
158
205
|
roles.add(role);
|
|
159
206
|
}
|
|
160
207
|
last = j;
|