@postalsys/email-content 0.1.8 → 0.1.9
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 +17 -8
- 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 +67 -14
- 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 +134 -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 +5 -70
- 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/reply-headers.json +368 -0
- package/dist/detect/reply-header.d.ts.map +1 -1
- package/dist/detect/reply-header.js +12 -1
- 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/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 +44 -0
- package/dist/eval/reference.d.ts.map +1 -0
- package/dist/eval/reference.js +87 -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/html-blocks.d.ts.map +1 -1
- package/dist/segment/html-blocks.js +11 -7
- 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 +14 -3
- package/dist/segment/text-blocks.js.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/package.json +2 -2
|
@@ -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": "*",
|
|
@@ -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;AASD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,SAAI,GAAG,gBAAgB,GAAG,IAAI,CA8EnG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CASjE"}
|
|
@@ -51,7 +51,18 @@ function valueOf(line) {
|
|
|
51
51
|
* one is almost always a paragraph that happens to end in "wrote:".
|
|
52
52
|
*/
|
|
53
53
|
function detectAttributionLine(line) {
|
|
54
|
-
|
|
54
|
+
// Clients that derive plain text from HTML write links out as "mailto:" URLs, and SnappyMail
|
|
55
|
+
// puts one inside every attribution, fully URL encoded: '"Marta Kask" <marta@example.com
|
|
56
|
+
// mailto:marta@example.com?to=%22Marta...%3E > schrieb:'. The URL says nothing about the line's
|
|
57
|
+
// shape but pushes it past the length limits, so it is dropped before matching. The raw line is
|
|
58
|
+
// bounded first so the replace stays cheap on hostile input.
|
|
59
|
+
if (line.length > 2000) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
// Only lines that contain a URL pay for the replace, and it has no leading whitespace class:
|
|
63
|
+
// "\s*" before the literal was retried at every position of a long space run, quadratic in
|
|
64
|
+
// its length (1 MB of such lines blocked for over a second).
|
|
65
|
+
const trimmed = (/mailto:/i.test(line) ? line.replace(/ ?\bmailto:\S+/gi, '') : line).trim();
|
|
55
66
|
if (!trimmed || trimmed.length > 300) {
|
|
56
67
|
return null;
|
|
57
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-header.js","sourceRoot":"","sources":["../../src/detect/reply-header.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AAmDH,
|
|
1
|
+
{"version":3,"file":"reply-header.js","sourceRoot":"","sources":["../../src/detect/reply-header.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subject prefixes that mark a reply or a forward, shared by analyze() and the corpus scanner so
|
|
3
|
+
* the two cannot drift apart again.
|
|
4
|
+
*
|
|
5
|
+
* Forward prefixes were measured on 266 real forwards from 16 clients (test/fixtures/clients):
|
|
6
|
+
* every one used Fwd, Fw or Ed (Outlook's Estonian prefix), whatever the interface language, and
|
|
7
|
+
* 250 of them also carried In-Reply-To or References, so headers cannot tell a forward from a
|
|
8
|
+
* reply. The other entries are the prefixes Outlook and webmail clients write in other locales.
|
|
9
|
+
* A reply to a forward reads "Re: Fwd:" and is caught by REPLY_SUBJECT first, which is also why
|
|
10
|
+
* "VS" (a Finnish reply, a Norwegian forward) is listed only as a reply.
|
|
11
|
+
*/
|
|
12
|
+
export declare const REPLY_SUBJECT: RegExp;
|
|
13
|
+
export declare const FORWARD_SUBJECT: RegExp;
|
|
14
|
+
/** A forward, and not a reply to one. */
|
|
15
|
+
export declare function isForwardSubject(subject: string): boolean;
|
|
16
|
+
//# sourceMappingURL=subject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../../src/detect/subject.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,aAAa,QAAgE,CAAC;AAE3F,eAAO,MAAM,eAAe,QAA0F,CAAC;AAEvH,yCAAyC;AACzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Subject prefixes that mark a reply or a forward, shared by analyze() and the corpus scanner so
|
|
4
|
+
* the two cannot drift apart again.
|
|
5
|
+
*
|
|
6
|
+
* Forward prefixes were measured on 266 real forwards from 16 clients (test/fixtures/clients):
|
|
7
|
+
* every one used Fwd, Fw or Ed (Outlook's Estonian prefix), whatever the interface language, and
|
|
8
|
+
* 250 of them also carried In-Reply-To or References, so headers cannot tell a forward from a
|
|
9
|
+
* reply. The other entries are the prefixes Outlook and webmail clients write in other locales.
|
|
10
|
+
* A reply to a forward reads "Re: Fwd:" and is caught by REPLY_SUBJECT first, which is also why
|
|
11
|
+
* "VS" (a Finnish reply, a Norwegian forward) is listed only as a reply.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.FORWARD_SUBJECT = exports.REPLY_SUBJECT = void 0;
|
|
15
|
+
exports.isForwardSubject = isForwardSubject;
|
|
16
|
+
exports.REPLY_SUBJECT = /^\s*(re|aw|sv|vs|vast|antw|res|odp|ответ)\s*(\[\d+\])?\s*:/i;
|
|
17
|
+
exports.FORWARD_SUBJECT = /^\s*(fwd?|wg|tr|rv|enc|doorst|pd|vl|ed|edasi|пересл[а-я]*|转发|轉寄|転送)\s*(\[\d+\])?\s*:/i;
|
|
18
|
+
/** A forward, and not a reply to one. */
|
|
19
|
+
function isForwardSubject(subject) {
|
|
20
|
+
return !exports.REPLY_SUBJECT.test(subject) && exports.FORWARD_SUBJECT.test(subject);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=subject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject.js","sourceRoot":"","sources":["../../src/detect/subject.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAOH,4CAEC;AAPY,QAAA,aAAa,GAAG,6DAA6D,CAAC;AAE9E,QAAA,eAAe,GAAG,uFAAuF,CAAC;AAEvH,yCAAyC;AACzC,SAAgB,gBAAgB,CAAC,OAAe;IAC5C,OAAO,CAAC,qBAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,uBAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern precision audit.
|
|
3
|
+
*
|
|
4
|
+
* Aggregate agreement says how the extractor does overall; it cannot say which pattern to fix.
|
|
5
|
+
* This groups reference-labelled blocks by the detector evidence they carry and by the reasons
|
|
6
|
+
* behind their final decision, so a single pattern that fires on authored text stands out even
|
|
7
|
+
* when the headline numbers look fine.
|
|
8
|
+
*
|
|
9
|
+
* Two views, because they answer different questions:
|
|
10
|
+
*
|
|
11
|
+
* - Evidence: every block a pattern fired on, whatever the rules then did with it. This is the
|
|
12
|
+
* pattern's own precision, the thing a pattern file edit changes.
|
|
13
|
+
* - Reasons: every block whose final decision names a reason. This is the rule's precision, and
|
|
14
|
+
* for hiding reasons the rows that matter most, since a wrong hide is content loss.
|
|
15
|
+
*
|
|
16
|
+
* Evidence sits on the block that matched, usually the attribution line or separator itself, so
|
|
17
|
+
* the evidence view measures whether the boundary was real, not how far the region after it ran.
|
|
18
|
+
* The reason view covers the region.
|
|
19
|
+
*/
|
|
20
|
+
import { type Label } from '../types';
|
|
21
|
+
/** One reference-labelled block with what the extractor saw and decided. Never carries content. */
|
|
22
|
+
export interface AuditBlock {
|
|
23
|
+
reference: Label;
|
|
24
|
+
predicted: Label;
|
|
25
|
+
hidden: boolean;
|
|
26
|
+
chars: number;
|
|
27
|
+
evidence: readonly {
|
|
28
|
+
id: string;
|
|
29
|
+
kind: string;
|
|
30
|
+
}[];
|
|
31
|
+
reasons: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
export interface AuditRow {
|
|
34
|
+
key: string;
|
|
35
|
+
/** Reference labels a hit is expected to carry, or null when the kind implies none. */
|
|
36
|
+
expected: readonly Label[] | null;
|
|
37
|
+
blocks: number;
|
|
38
|
+
chars: number;
|
|
39
|
+
/** Blocks whose reference label is in `expected`. */
|
|
40
|
+
matching: number;
|
|
41
|
+
/** Blocks precision is measured over: all of them, less those `neutral` excludes. */
|
|
42
|
+
scored: number;
|
|
43
|
+
/** Blocks the reference says the sender wrote: NEW_CONTENT or INLINE_QUOTE. */
|
|
44
|
+
authored: number;
|
|
45
|
+
/** Authored blocks the extractor hid. Each one is lost content. */
|
|
46
|
+
authoredHidden: number;
|
|
47
|
+
authoredHiddenChars: number;
|
|
48
|
+
byReference: Partial<Record<Label, number>>;
|
|
49
|
+
}
|
|
50
|
+
export interface PatternAudit {
|
|
51
|
+
blocks: number;
|
|
52
|
+
evidence: AuditRow[];
|
|
53
|
+
reasons: AuditRow[];
|
|
54
|
+
}
|
|
55
|
+
export declare function auditPatterns(blocks: readonly AuditBlock[]): PatternAudit;
|
|
56
|
+
//# sourceMappingURL=pattern-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-audit.d.ts","sourceRoot":"","sources":["../../src/eval/pattern-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,mGAAmG;AACnG,MAAM,WAAW,UAAU;IACvB,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,QAAQ,EAAE,SAAS,KAAK,EAAE,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,OAAO,EAAE,QAAQ,EAAE,CAAC;CACvB;AAoED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,YAAY,CAkCzE"}
|