@bobfrankston/rmfmail 1.1.129 → 1.1.130
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/.commitmsg +16 -0
- package/README.md +69 -12
- package/bin/mailx.js +112 -17
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +114 -17
- package/client/app.bundle.js +11 -7
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +22 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +21 -1
- package/client/components/message-viewer.js +5 -10
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +5 -10
- package/package.json +1 -1
- package/packages/mailx-imap/index.d.ts +15 -0
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +102 -0
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +84 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-21308 → node_modules.npmglobalize-stash-40492}/.package-lock.json +0 -0
|
@@ -4166,11 +4166,95 @@ export class ImapManager extends EventEmitter {
|
|
|
4166
4166
|
private outboxBackoff = new Map<string, number>(); // accountId → next retry timestamp
|
|
4167
4167
|
private outboxBackoffDelay = new Map<string, number>(); // accountId → current delay ms
|
|
4168
4168
|
|
|
4169
|
+
/** Route a file dropped into the general `~/.rmfmail/outbox/` (no acct
|
|
4170
|
+
* subdir) to one of the configured accounts. Match order:
|
|
4171
|
+
* 1. `From:` address matches an `account.email` exactly (case-insensitive).
|
|
4172
|
+
* 2. `From:` domain matches a known-provider domain → first account whose
|
|
4173
|
+
* domain matches (gmail.com / googlemail.com → first Gmail account;
|
|
4174
|
+
* outlook.com / hotmail.com / live.com → first Outlook account).
|
|
4175
|
+
* 3. Default = first account with an explicit IMAP override host (i.e.,
|
|
4176
|
+
* not one of the known providers). Bob's bobma is the prototype.
|
|
4177
|
+
* Returns the routed accountId or null if no candidate exists. */
|
|
4178
|
+
private routeGeneralOutboxFile(filePath: string): string | null {
|
|
4179
|
+
let raw = "";
|
|
4180
|
+
try { raw = fs.readFileSync(filePath, "utf-8"); } catch { return null; }
|
|
4181
|
+
const fromMatch = raw.match(/^From:\s*(?:[^<\n]*<\s*)?([^\s<>@]+@[^\s<>]+)/mi);
|
|
4182
|
+
const fromAddr = (fromMatch?.[1] || "").toLowerCase().replace(/[>\s].*$/, "");
|
|
4183
|
+
const fromDomain = fromAddr.split("@")[1] || "";
|
|
4184
|
+
|
|
4185
|
+
const settings = loadSettings();
|
|
4186
|
+
const accounts = settings.accounts || [];
|
|
4187
|
+
// 1. exact email match
|
|
4188
|
+
if (fromAddr) {
|
|
4189
|
+
const m = accounts.find(a => (a.email || "").toLowerCase() === fromAddr);
|
|
4190
|
+
if (m) return m.id;
|
|
4191
|
+
}
|
|
4192
|
+
// 2. known-provider domain match
|
|
4193
|
+
const GMAIL_DOMAINS = new Set(["gmail.com", "googlemail.com"]);
|
|
4194
|
+
const OUTLOOK_DOMAINS = new Set(["outlook.com", "hotmail.com", "live.com", "msn.com"]);
|
|
4195
|
+
if (fromDomain) {
|
|
4196
|
+
if (GMAIL_DOMAINS.has(fromDomain)) {
|
|
4197
|
+
const m = accounts.find(a => GMAIL_DOMAINS.has((a.email || "").split("@")[1]?.toLowerCase() || ""));
|
|
4198
|
+
if (m) return m.id;
|
|
4199
|
+
}
|
|
4200
|
+
if (OUTLOOK_DOMAINS.has(fromDomain)) {
|
|
4201
|
+
const m = accounts.find(a => OUTLOOK_DOMAINS.has((a.email || "").split("@")[1]?.toLowerCase() || ""));
|
|
4202
|
+
if (m) return m.id;
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
// 3. default: first account on a non-known-provider domain (i.e. one
|
|
4206
|
+
// whose IMAP host was explicitly configured rather than auto-detected).
|
|
4207
|
+
const KNOWN_PROVIDER_DOMAINS = new Set([
|
|
4208
|
+
...GMAIL_DOMAINS, ...OUTLOOK_DOMAINS,
|
|
4209
|
+
"yahoo.com", "aol.com", "icloud.com", "me.com", "mac.com",
|
|
4210
|
+
]);
|
|
4211
|
+
const override = accounts.find(a => {
|
|
4212
|
+
const d = (a.email || "").split("@")[1]?.toLowerCase() || "";
|
|
4213
|
+
return d && !KNOWN_PROVIDER_DOMAINS.has(d);
|
|
4214
|
+
});
|
|
4215
|
+
if (override) return override.id;
|
|
4216
|
+
// Last resort — any account at all
|
|
4217
|
+
return accounts[0]?.id || null;
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
/** Scan the general outbox (`~/.rmfmail/outbox/*.ltr|*.eml` — no acct
|
|
4221
|
+
* subdir) and route each file into the appropriate per-account dir. Runs
|
|
4222
|
+
* once per outboxLoop tick before the per-account sweep, so a file
|
|
4223
|
+
* manually dropped at the root gets handed off the same tick. */
|
|
4224
|
+
private routeGeneralOutbox(): void {
|
|
4225
|
+
const root = path.join(getConfigDir(), "outbox");
|
|
4226
|
+
if (!fs.existsSync(root)) return;
|
|
4227
|
+
let entries: fs.Dirent[] = [];
|
|
4228
|
+
try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return; }
|
|
4229
|
+
for (const ent of entries) {
|
|
4230
|
+
if (!ent.isFile()) continue;
|
|
4231
|
+
if (!/\.(ltr|eml)$/i.test(ent.name)) continue;
|
|
4232
|
+
const filePath = path.join(root, ent.name);
|
|
4233
|
+
const accountId = this.routeGeneralOutboxFile(filePath);
|
|
4234
|
+
if (!accountId) {
|
|
4235
|
+
console.error(` [outbox] No account candidate for ${ent.name} — leaving in general outbox`);
|
|
4236
|
+
continue;
|
|
4237
|
+
}
|
|
4238
|
+
const acctDir = path.join(root, accountId);
|
|
4239
|
+
try { fs.mkdirSync(acctDir, { recursive: true }); } catch { /* */ }
|
|
4240
|
+
try {
|
|
4241
|
+
fs.renameSync(filePath, path.join(acctDir, ent.name));
|
|
4242
|
+
console.log(` [outbox] Routed ${ent.name} → ${accountId}/`);
|
|
4243
|
+
} catch (e: any) {
|
|
4244
|
+
console.error(` [outbox] Failed to route ${ent.name}: ${e.message}`);
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4169
4249
|
startOutboxWorker(): void {
|
|
4170
4250
|
if (this.outboxInterval) return;
|
|
4171
4251
|
|
|
4172
4252
|
const processAll = async () => {
|
|
4173
4253
|
const now = Date.now();
|
|
4254
|
+
// Auto-route any files dropped into the general (acct-agnostic)
|
|
4255
|
+
// outbox — they move into `outbox/<accountId>/` before the
|
|
4256
|
+
// per-account sweep picks them up below.
|
|
4257
|
+
this.routeGeneralOutbox();
|
|
4174
4258
|
for (const [accountId] of this.configs) {
|
|
4175
4259
|
// Skip accounts in backoff
|
|
4176
4260
|
const retryAfter = this.outboxBackoff.get(accountId) || 0;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bobfrankston/mailx-imap",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.58",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@bobfrankston/iflow-direct": "^0.1.27",
|